4

(this question is not related to C++11/C++14: the examples are compiled using C++03)

enable_bool<T> has a member ::type only when T is bool

template <class T>
struct enable_bool
{};

template <>
struct enable_bool< bool >
{ typedef bool type; };

In the next snippet, the partial specialization is correct (see gcc.godbolt.org)

template <class T, class U, class Enable = T>
struct Foo
{
    static int bar() { return 0; }
};

template <class T, class U>
struct Foo< T, U, typename enable_bool<T>::type >
{
    static int bar() { return 1; }
};

int main()
{
    return Foo <int, bool>::bar();
}

As enable_bool<T>::type already corresponds to T (when T is bool)
we are tempted to factorize parameters T and Enable.
But compiler complains (see gcc.godbolt.org)

template <class T, class U>
struct Foo
{
    static int bar() { return 0; }
};

template <class T, class U> //ERROR non-deducible template parameter 'T'
struct Foo< typename enable_bool<T>::type, U >
{
    static int bar() { return 1; }
};

Why compiler cannot deduce template parameter T in this above partial specialization?

oHo
  • 51,447
  • 27
  • 165
  • 200
  • 2
    Dupe. tl;dr: yes, it's "specified in the C++ standard" in that it's a fundamental fact of how `std::enable_if` actually works. No, it won't "evolve" in C++17. – Lightness Races in Orbit Oct 07 '15 at 16:56
  • Hi @LightnessRacesinOrbit. Thanks for your comment but question [std::enable_if to conditionally compile a member function](http://stackoverflow.com/questions/6972368/stdenable-if-to-conditionally-compile-a-member-function) is not the same. In his answer, Johannes says *"`std::enable_if<>::type` accesses a non-existing type, so that declaration is ill-formed"*. This is maybe the answer of my question. I wonder about the C++ Standard/Compiler reason to do not allow one single parameter for SFINAE (using `std::enable_if` or not). Please reopen my question. Cheers – oHo Oct 07 '15 at 17:07
  • It is my way of informing you that this requirement is due to how `std::enable_if` works. Therefore, to answer the question, you should read how `std::enable_if` works; certainly there is little to gain in our repeating all of that here. I did actually read the answers there (albeit briefly), and confirmed that they make reference to this particular requirement, within the broader scope and context of the feature in question. – Lightness Races in Orbit Oct 07 '15 at 17:13
  • However, if you'd like to come back in a couple of days having studied that post, with _specific_ queries that relate to the information therein, I'd be happy to take another look. – Lightness Races in Orbit Oct 07 '15 at 17:15
  • Hi @LightnessRacesinOrbit. I have read questions/answers and `std::enable_if` documentation. But it is not obvious where the answer is. (Please give me the answer). I think it will not help SO users to be redirected to the other question (because the answer of my question is not blinking there). I have fully rewritten my question since first post. My question is not related to C++11 neither to `std::enable_if`. While rewriting/testing the code I got a light. I think the answer is because compiler needs to know where is the parameter `int`. If you reopen the question I can provide an answer. – oHo Oct 08 '15 at 13:45
  • The issue is unrelated to `enable_if`s implementation. Rather, the form of the pattern used in the partial specialization, `typename some_template::type`, is specifically forbidden from deducing `T` by C++03[temp.deduct.type]p4. The rationale: You need the nested `::type` to match against the template argument, and finding that type requires instantiating *all* specializations of the template (and might be ambiguous, produce deep errors). There's no special rule for simple templates such as `enable_if`; and if there was, that would make `enable_if` useless. – dyp Oct 08 '15 at 16:02
  • Hi @dyp. Thanks for your comment. I have just fully reworked my question. Doing this simplification work (in my head and in SO) I realized that compiler cannot deduce `T` when trying to match `typename enable_bool::type` and `int`. I agree: my question is not related to SFINAE. Do you want to provide an answer? – oHo Oct 08 '15 at 16:11
  • @olibre Well if you have come to this conclusion yourself, then I think there's no need for me to write an answer. But feel free to write an answer yourself. – dyp Oct 08 '15 at 18:20

1 Answers1

2

Finally the question was not even related to SFINAE!

Consider this very simple snippet without SFINAE:
enable<T>::type is always same as T whatever the provided T type.

template <class T>
struct enable
{ typedef T type; };       // Enable always

template <class U, class V>
struct Foo
{
    static int bar() { return 0; }
};

template <class X, class Y> //ERROR non-deducible parameter 'X'
struct Foo< typename enable<X>::type, Y >
{
    static int bar() { return 1; }
};

int main()
{
    return Foo<int, bool>::bar();
}

When compiler tries to match Foo<int, bool> with Foo<typename enable<X>::type,Y>

  • 1st param U = int <--> enable<X>::type => Cannot deduce X
  • 2nd param V = bool <--> Y

Compiler is not designed to deduce X from equation int = enable<X>::type.
Therefore, compiler needs some help from developer.
Another parameter is required: Enable.

The below fixed snippet add the Enable class template parameter.
The compiler performs the following matching:

  • 1st param U =int <--> X
  • 2nd param V =bool<--> Y
  • 3rd param Enable=int <--> enable<X>::type (deduced X from 1st param)
    (3rd param is int because declaration class Enable=U means by default 3rd param same as 1st one)

Fixed snippet:

template <class T>
struct enable
{ typedef T type; };       // Enable always

template <class U, class V, class Enable = U>
struct Foo
{
    static int bar() { return 0; }
};

template <class X, class Y> // Compiler can deduce 'X'
struct Foo< X, Y, typename enable<X>::type >
{
    static int bar() { return 1; }
};

int main()
{
    return Foo<int, bool>::bar();
}
oHo
  • 51,447
  • 27
  • 165
  • 200