1

I'm trying to do SFINAE on a constructor. I want to enable one overload for integers and one for everything else. I know I can just make a base(int) and base(T) constructor but I want to do it this way instead.

template <class T>
struct base
{
    template <class T1 = T>
    base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}

    template <class T1 = T>
    base(T1, typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr) {}
};

Then I make a main Base class that inherits the constructors:

template <class T>
struct Base : base<T>
{
    using base<T>::base;
};

But when I instantiate Base with any T I get these errors:

source_file.cpp:21:15: error: call to deleted constructor of 'Base<int>'
    Base<int> v(4);
              ^ ~
source_file.cpp:16:25: note: deleted constructor was inherited here
    using base<T>::base;
                        ^
source_file.cpp:7:5: note: constructor cannot be inherited
    base(T1, typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr) {}
    ^

When I instantiate base directly it works without a problem. Why can't the constructor be inherited when I am doing SFINAE? Without the second constructor overload everything works fine.

Me myself and I
  • 3,990
  • 1
  • 23
  • 47
  • The error messages refer to `base_impl` but your source doesn't provide it - just a copy and paste issue? (i.e. is `base` named `base_impl` in your actual code?) – sfjac Nov 09 '14 at 23:44
  • @sfjac I just changed names slightly. `base` is actually `base_impl`. – Me myself and I Nov 09 '14 at 23:48
  • 1
    Inheriting constructors [break really badly with SFINAE using default arguments](http://stackoverflow.com/a/25657097/2756719). Just...don't do it. – T.C. Nov 09 '14 at 23:58
  • @T.C. Thanks. That sucks because I think for my situation they're needed. – Me myself and I Nov 10 '14 at 00:09
  • possible duplicate of [Constructor inheritance failure with boost::multiprecision::mpz\_int](http://stackoverflow.com/questions/24912280/constructor-inheritance-failure-with-boostmultiprecisionmpz-int) – Me myself and I Nov 10 '14 at 00:52

2 Answers2

2

You could avoid this problem altogether in the example program by defining only the generic constructor in base and providing a specialization for base<int> (DEMO):

template <class T>
struct base
{
    base(T) { std::cout << "generic constructor\n"; }
};

template <>
base<int>::base(int) { std::cout << "int specialization\n"; }

or use tag dispatching instead of SFINAE (DEMO):

template <class T>
class base
{
    base(std::true_type, int) { std::cout << "int specialization\n"; }
    base(std::false_type, T) { std::cout << "generic constructor\n"; }

public:
    base(T t) : base(std::is_same<int, T>{}, std::move(t)) {}
};
Casey
  • 41,449
  • 7
  • 95
  • 125
0

Unlike the parameter list of a constructor, the entire template parameter list is always inherited including default template arguments. Put the SFINAE there:

template <class T>
struct base
{
    template <class T1 = T,
      typename std::enable_if<std::is_same<T1, int>::value>::type* = nullptr>
    base(T1) {}

    template <class T1 = T,
      typename std::enable_if<!std::is_same<T1, int>::value>::type* = nullptr>
    base(T1) {}
};

This works in Clang and GCC, in -std=c++11 and -std=c++1z modes. (Only recent versions tested, 3.6 and 5.1, respectively.)

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421