Uzaku
Uzaku

Reputation: 541

troubles with SFINAE

I tried to overload the operator- so that it applies set difference to its arguments. Since I couldn't figure out how to restrict it to STL containers only (because If I dont, it will overwriter every operator- it seems), I tried to restrict it to set and vector only, because I'd use it with those two. Here is my code:

#define RANGE(x) (x).begin(), (x).end()

template<class T>
struct is_STL_container
{
    static const bool value = false;
};

template<class T, typename alloc>
struct is_STL_container<std::vector<T, alloc>>
{
    static const bool value = true;
};

template<class T, class comp, typename alloc>
struct is_STL_container<std::set<T, comp, alloc>>
{
    static const bool value = true;
};


template <class T1, class T2,
class std::enable_if<is_STL_container<T1>::value && is_STL_container<T2>::value, T1>::type>
T1 operator - (const T1 &l, const T2 &r)
{
    assert(typeid(T1::value_type) == typeid(T2::value_type));

    std::vector<T1::value_type> result;
    std::set_difference(RANGE(l), RANGE(r), std::back_inserter(result));
    return T1(RANGE(result));
}

But when I try to compile my program i get the following errors:

Error   2   error C1903: unable to recover from previous error(s); stopping compilation C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm    3071    1   TrafficLight
Error   1   error C2893: Failed to specialize function template 'unknown-type std::less<void>::operator ()(_Ty1 &&,_Ty2 &&) const'  C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm    3071    1   TrafficLight

I can't figure out the problem Im using VS2013

Best Regards Uzaku

Upvotes: 0

Views: 115

Answers (3)

James Adkison
James Adkison

Reputation: 9602

There are a number of syntax errors with your code. The following example below works.

Note: The operator- below is templated on T instead of T1 and T2 because I wasn't sure you actually wanted to allow operator- to work when using vectors of different types, e.g. std::vector<int> and std::vector<double>.

Example Code

#include <algorithm>
#include <iostream>
#include <set>
#include <vector>

#define RANGE(x) (x).begin(), (x).end()

template<class T>
struct is_STL_container
{
    static const bool value = false;
};

template<class T, typename alloc>
struct is_STL_container<std::vector<T, alloc>>
{
    static const bool value = true;
};

template<class T, class comp, typename alloc>
struct is_STL_container<std::set<T, comp, alloc>>
{
    static const bool value = true;
};

template <class T>
typename std::enable_if<is_STL_container<T>::value && is_STL_container<T>::value, T>::type
operator-(const T &l, const T &r)
{
    T result;
    std::set_difference(RANGE(l), RANGE(r), std::back_inserter(result));
    return result;
}

int main()
{
    std::vector<int> a = { 1, 2, 3, 4, 5 };
    std::vector<int> b = { 2, 3, 4 };
    std::vector<int> r = a - b;
    for (const auto& v : r)
    {
        std::cout << v << " ";
    }

    return 0;
}

Example Output

1 5 

Live Example


Edit 1

Note: As pointed out in the comments by @StoryTeller, templating operator- on just T would prevent the valid usage of operator- between std::vector<int> and std::set<int> (as attempted by the OP). The following code uses T1 and T2 to resolve this issue.

Example Code

#include <algorithm>
#include <cassert>
#include <iostream>
#include <set>
#include <typeinfo>
#include <vector>

#define RANGE(x) (x).begin(), (x).end()

template<class T>
struct is_STL_container
{
    static const bool value = false;
};

template<class T, typename alloc>
struct is_STL_container<std::vector<T, alloc>>
{
    static const bool value = true;
};

template<class T, class comp, typename alloc>
struct is_STL_container<std::set<T, comp, alloc>>
{
    static const bool value = true;
};


template <class T1, class T2>
typename std::enable_if<is_STL_container<T1>::value && is_STL_container<T2>::value, T1>::type
operator-(const T1 &l, const T2 &r)
{
    assert(typeid(typename T1::value_type) == typeid(typename T2::value_type));

    T1 result;
    std::set_difference(RANGE(l), RANGE(r), std::back_inserter(result));
    return result;
}

int main()
{
    std::vector<int> a = { 1, 2, 3, 4, 5 };
    std::vector<int> b = { 2, 3, 4 };
    std::set<int> c = { 2, 3, 4 };

    std::vector<int> r = a - b;
    for (const auto& v : r)
    {
        std::cout << v << " ";
    }

    std::cout << "\n";

    r = a - c;
    for (const auto& v : r)
    {
        std::cout << v << " ";
    }

    return 0;
}

Example Output

1 5 
1 5 

Live Example

Upvotes: 4

n. m. could be an AI
n. m. could be an AI

Reputation: 119847

Consider this definition

template <class T1, class T2, class int> int operator- ...

This is blatantly incorrect, right? But you have written only slightly more complicated version of it.

What you neef is

template <class T1, class T2>
typename std::enable_if<whatever>::type
operator- ...

Upvotes: 1

std::enable_if is a metafuction returning a type (defaulted to void).
As such, you main operator- statement is malformed.

You must change the template class parameter to look like this

class = typename std::enable_if_t<is_STL_container<T1>::value &&
                                  is_STL_container<T2>::value>

Or this:

class = typename std::enable_if<is_STL_container<T1>::value &&
                                is_STL_container<T2>::value>::type

You can find the fixed program here.

Upvotes: 3

Related Questions