2

Stroustrup C++ 4th Ed Page 796 states that "If Enable_if’s condition evaluates to false, the whole function declaration of which it is part is completely ignored." and "...we don’t declare anything.".

Does anyone know why the first f0() is being declared? Am I using enable_if correctly?

My goal is to disable one of the declarations. I'm not sure how both f0() can have a return type, as the false version should be missing ::type.

#include <type_traits>
using namespace std;

template<bool B, typename T>
using Enable_if = typename std::enable_if<B,T>::type;

template <class T>
class X {
    Enable_if<false, T> f0(int x) {};
    Enable_if<true, T> f0(int x) {};
};

int main(void)
{
    X<void> xx;
    return 0;
}

Compilation:

clang++ -std=c++11 -Wall -pedantic test197.cc && ./a.out
test197.cc:10:24: error: functions that differ only in their return type cannot
      be overloaded
    Enable_if<true, T> f0(int x) {};
    ~~~~~~~~~~~~~~~~~~ ^
test197.cc:9:25: note: previous definition is here
    Enable_if<false, T> f0(int x) {};
    ~~~~~~~~~~~~~~~~~~~ ^
1 error generated.
asmmo
  • 6,922
  • 1
  • 11
  • 25
notaorb
  • 1,944
  • 1
  • 8
  • 18
  • 1
    SFINAE generally requires that the relevant declaration be a template itself. In this case, neither `f0` overload is a template; that they are members of a template type is not enough. – cdhowie Jul 26 '20 at 00:55

1 Answers1

2

You mustn't use this way because your object will be created in any case since the class doesn't have a condition to be created or not. And then here Enable_if<false, T> f0(int x) {};, the condition is false then there is no type. note that template class is instantiated on passing your parameter in the beginning and all that has done is replacing T by void.

one way you can use is as follows

#include <type_traits>
using namespace std;

template<bool B, typename T>
using Enable_if = typename std::enable_if<B,T>::type;


struct X {
    template <class T>
    Enable_if<true, T> f0(int x) {}
    template <class T>
    Enable_if<false, T> f0(int x) {}
};

int main(void)
{
    X xx;
    xx.f0<void>(4);
    return 0;
}

Here we have two function templates, hence each one of them may be instantiated or not and this depends on the condition inside the class. If it's true, the function will be instantiated and vice versa. note that here only the needed function is instantiated.

asmmo
  • 6,922
  • 1
  • 11
  • 25
  • This clears up the usage. I'm trying to understand how the false case prevents the declaration and why a template parameter is required. I think SFINAE occurs only with class parameters, so not sure that's happening here. Do you know? – notaorb Jul 26 '20 at 00:56
  • @JeJe can you rephrase your last sentence? I didn't understand it. – notaorb Jul 26 '20 at 01:04
  • @JeJo I'm referencing the answer posted in this thread. I understand SFINAE only works if substitution in argument deduction of a template argument makes the construct ill-formed. In such cases you declare `template ::value* = nullptr> void f0() {}` for instance. In above example in this thread, I do not see where "substitution in argument deduction of a template argument" occurs. That is my confusion and the duplicate does not answer this – notaorb Jul 26 '20 at 01:26
  • To summarize, what template parameter has a substitution failure in asmmo's example in this thread? If `T` how is this possible because the return type is not deduced? – notaorb Jul 26 '20 at 01:30
  • @notarob if it fails, the temple won't be instantiated at all, hence there's no substitution. – asmmo Jul 26 '20 at 01:41
  • @asmmo So it's the declaration itself that is ill formed and causes failure, causing non instantiation of the template? I.e. the declaration is missing a return ::type? In that case, I'm assuming the duplicate mention of "if substitution in argument deduction of a template argument makes the construct ill-formed" applies only to a construct like this one `template ::value* = nullptr> void f0() {}`. I.e. where a template argument is ill formed. – notaorb Jul 26 '20 at 01:48