-1

There was a question somewhat related to my problem earlier, which dealt with template class, that used std::enable_if on a method, which is declared in class prototype, but the actual implementation is done outside.

Source: function implementation with enable_if outside of class definition

I want to do something similar, but with the class constructor, which I want to define outside template class with std::enable_if metafunction.

template <typename T>
using EnableIfArithmetic = typename std::enable_if<std::is_arithmetic<T>::value, void>::type;

template <typename NumericType>
class SomeClass {
public:
    // constructor definition
    template <typename = EnableIfArithmetic<NumericType>>
    SomeClass() {
        // do some stuff
    }
};

Desired form:

template <typename NumericType>
class SomeClass {
public:
     // constructor declaration
     template <typename = EnableIfArithmetic<NumericType>>
     SomeClass();
};

// constructor definition
template <typename NumericType>
template <typename = EnableIfArithmetic<NumericType>>
SomeClass<NumericType>::SomeClass() {
        // constructor implementation
}

But I cannot get it right, without compiling error. What am I doing wrong?

Evg
  • 25,259
  • 5
  • 41
  • 83
L'ahim
  • 43
  • 4
  • Post the error and a [MCVE]. – Vittorio Romeo Oct 24 '18 at 09:12
  • 2
    Just omit the default value, `EnableIfArithmetic`, in the definition. – Evg Oct 24 '18 at 09:12
  • Given how you seem to want to use SFINAE in this code, I don't understand why you don't drop in a static_assert. There is no alternative substitution here, so this effectively looks like nothing more than a compile-time aid to bark an error if the argument is an arithmetic type, which a static assert would do. – WhozCraig Oct 24 '18 at 09:41

1 Answers1

1

Default values for the template arguments should not be repeated in the definitions. For example:

template<typename N>
struct S {
    template<typename T, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
    S(T);
};

template<typename N>
template<typename T, typename>
S<N>::S(T) { }

The way you use SFINAE is not correct: EnableIfArithmetic should depend on some deduced type (in the same template). Please refer to this question. For example:

template<typename T = N, typename = EnableIfArithmetic<T>>
S() { }

Otherwise, a hard fail will occur:

error: no type named 'type' in 'struct std::enable_if'

If you want to disable the default constructor for some types N, you can also use static_assert inside the constructor. However, it won't be SFINAE friendly.

template<typename N>
struct S1 {
public:
    template<typename T = N, typename = std::enable_if_t<std::is_arithmetic_v<T>>>
    S1() { }
};

template<typename N>
struct S2 {
public:
    S2() { static_assert(std::is_arithmetic_v<N>); }
};

static_assert(std::is_default_constructible_v<S1<int>>);
static_assert(!std::is_default_constructible_v<S1<void>>);

static_assert(std::is_default_constructible_v<S2<int>>);
static_assert(std::is_default_constructible_v<S2<void>>);
Evg
  • 25,259
  • 5
  • 41
  • 83