codeJack
codeJack

Reputation: 2503

c++03: Mutually exclusive methods thanks to enable_if

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'

Upvotes: 2

Views: 777

Answers (4)

max66
max66

Reputation: 66240

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"
 }

Upvotes: 0

Alexey
Alexey

Reputation: 1226

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.

Upvotes: 0

Curious
Curious

Reputation: 21540

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

Upvotes: -1

Jarod42
Jarod42

Reputation: 217930

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();
};

Upvotes: 1

Related Questions