2

Within a class, I have two different methods which should be mutually exclusive depending on the caller template parameter.

class Foo
{
    // For collections
    template<class T>
    typename boost::enable_if<boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
    doSomething()
    { }


    // For single types
    template<class T>
    typename boost::enable_if<!boost::is_same<typename std::vector<typename T::value_type>, T>::value, const T&>::type
    doSomething()
    { }
}

This won't compile.

error: type/value mismatch at argument 1 in template parameter list for 'template struct boost::enable_if' error: expected a type, got '! boost::is_same::value'

Jarod42
  • 203,559
  • 14
  • 181
  • 302
codeJack
  • 2,423
  • 5
  • 24
  • 31

4 Answers4

1

How about:

template <typename T> struct is_std_vector : std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> : std::true_type {};

And then

class Foo
{
    // For collections
    template<class T>
    typename std::enable_if<is_std_vector<T>::value, const T&>::type
    doSomething();

    // For single types
    template<class T>
    typename std::enable_if<!is_std_vector<T>::value, const T&>::type
    doSomething();
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • This kinda works. But why couldn't I use my own syntax with no need of defining a is_std_vector struct ? – codeJack Jun 19 '17 at 08:11
  • 1
    `T::value_type` is not defined for all type, So you SFINAE also on that, and it is incorrect for most non-container types. – Jarod42 Jun 19 '17 at 08:20
0

Unlike std's version, boost::enable_if accepts a type (kinda wrapper under boolean value), so you should write something like

class Foo
{
    // For collections
    template<class T>
    typename boost::enable_if<
        typename boost::is_same<typename std::vector<typename T::value_type>, T>,
    const T&>::type doSomething()
    { }


    // For single types
    template<class T>
    typename boost::enable_if_с<
        !boost::is_same<typename std::vector<typename T::value_type>, T>::value,
    const T&>::type doSomething()
    { }
}

Note here, I've used typename before boost::is_same and haven't used ::value in the first specification. On the contrary, I had to use enable_if_с in the second overload, because ! operator isn't applicable to a type.

Alexey
  • 1,198
  • 1
  • 15
  • 35
0

What about a sort of tag dispatching?

#include <vector>
#include <iostream>

template <typename, typename>
struct isSame
 { typedef int type; };

template <typename T>
struct isSame<T, T>
 { typedef long type; };

struct foo
 {
   template <typename T>
   T const & doSomething (T const & t, int)
    { std::cout << "int version" << std::endl; return t; }

   template <typename T>
   T const & doSomething (T const & t, long)
    { std::cout << "long version" << std::endl; return t; }

   template <typename T>
   T const & doSomething (T const & t)
    { return doSomething(t, typename isSame<
        typename std::vector<typename T::value_type>, T>::type()); }
 };

int main ()
 {
   foo f;
   std::vector<int> v;
   f.doSomething(v);   // print "long version"
 }
max66
  • 65,235
  • 10
  • 71
  • 111
-1

If what you want is to overload the function based on whether you are given a vector or not

#include <type_traits>
#include <iostream>
#include <vector>

using std::cout;
using std::endl;

class Foo {
public:
    // For collections
    template <class T>
    const vector<T>& do_something(const std::vector<T>& input) {
        cout << __PRETTY_FUNCTION__ << endl;
        return input;
    }


    // For single types
    template <class T>
    const T& do_something(const T& input) {
        cout << __PRETTY_FUNCTION__ << endl;
        return input;
    }
};

int main() {
    auto foo = Foo{};
    auto v = std::vector<int>{};
    auto i = int{};
    foo.do_something(v);
    foo.do_something(i);
}

If you want to be even more general and check for any instantiated type

#include <type_traits>
#include <iostream>
#include <vector>

using std::cout;
using std::endl;

namespace {

    template <typename T, template <typename...> class TT>
    struct IsInstantiationOf
            : public std::integral_constant<bool, false> {};
    template <template <typename...> class TT, typename... Args>
    struct IsInstantiationOf<TT<Args...>, TT>
            : public std::integral_constant<bool, true> {};
} // namespace anonymous

class Foo {
public:
    // For collections
    template <typename VectorType, typename std::enable_if_t<IsInstantiationOf<
            std::decay_t<VectorType>, std::vector>::value>* = nullptr>
    void do_something(VectorType&&) {
        cout << "Vector overload" << endl;
    }

    // For single types
    template <class T, typename std::enable_if_t<!IsInstantiationOf<
            std::decay_t<T>, std::vector>::value>* = nullptr>
    void do_something(T&&) {
        cout << "Non vector overload" << endl;
    }
};

int main() {
    auto foo = Foo{};
    auto v = std::vector<int>{};
    auto i = int{};
    foo.do_something(v);
    foo.do_something(i);
}

Also please note that you should avoid putting std::enable_if in the function signature as much as possible for these reasons https://stackoverflow.com/a/14623831/5501675

Curious
  • 20,870
  • 8
  • 61
  • 146
  • The return type of the function is the templated type T, I cannot sort it out with simple function overloading, I need SFINAE – codeJack Jun 19 '17 at 07:44
  • @codeJack edited again to use SFINAE, if that is what you want – Curious Jun 19 '17 at 07:48
  • No c++03 and you changed the signature of the functions to have the template parameter withing the signature. It could be a way to go but it does not take into account all the requirements – codeJack Jun 19 '17 at 08:45
  • @codeJack its pretty easy to replace my `std::enable_if` with `boost::enable_if_c` the concepts are the same – Curious Jun 19 '17 at 08:49