Question

The following code

#include <iostream>
#include <utility>

template<typename F, typename... T>
struct Wrapper{ };

template<typename T>
struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};

int main()
{
    Wrapper<int, double> w;

    std::cout << is_wrapper<decltype(w)>::value << std::endl;
}

prints 0. However, if one uncomments the two lines in the middle it prints 1.

Why doesn't it always print 1? Shouldn't the second partial specialization also cover the case which is apparently only covered by the third (commented) partial specialization?

Was it helpful?

Solution

The code should indeed match the partial specialization; The standard never actually disallowed this, but compilers did take a while to implement variadic templates and their deduction properly. GCC conforms since 4.9.0 and Clang as of 3.6. The relevant bug report for Clang is #22191 (I cannot find GCC's, though).

OTHER TIPS

Normally when I write templates that would be specialized, I use a forward declaration first, and declare the cases belong as specializations. In your case, I understand that you are trying to write a variadic template without the empty-case (That is, a variadic template wich could have at least one type).

Your code surprised me because I think that you are correct, the full variadic specialization of your trait matches the hole cases... First I tried to use a forward declaration of your trait class, and define ONLY the full-variadic specialization (So if the param of the trait is not a Wrapper instance the compilation fails). And thats exactly what has ocurred, disappointing me again:

#include <iostream>
#include <utility>

template<typename F , typename... T>
struct Wrapper {};

template<typename T>
struct is_wrapper;

//template<typename T>
//struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};

using my_wrapper_type = Wrapper<int,double>;

int main()
{   
    std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
}//"Invalid use of incomplete type" ^^^^^^^^^^^^^^^^^^^^^^^^^^^

Finally I tried with the forward declaration method in the Wrapper class. And surprisingly, it works:

#include <iostream>
#include <utility>

template<typename... T>
struct Wrapper;

template<typename F, typename... T>
struct Wrapper<F,T...>{ };


template<typename T>
struct is_wrapper : std::false_type {};

template<typename... T>
struct is_wrapper<Wrapper<T...>> : std::true_type {};

//template<typename F, typename... T>
//struct is_wrapper<Wrapper<F, T...>> : std::true_type {};


using my_wrapper_type = Wrapper<int,double>;

int main()
{
    std::cout << std::boolalpha  << is_wrapper<my_wrapper_type>::value << std::endl;
}

This prints:

true

Here is the code running at ideone.

Sincerely, I don't understand why your code fails and mine works. Its a compiler bug, or there is something what we are missing? I don't know.

If I understand well your problem it's just a specialization priority,

is_wrapper<decltype(w)>

Can be specialized by 2 template :

template<typename T> // exact specialization
template<typename... T> // variadic specialization with one parameter 

In this case compiler choose in priority the exact specialization and so template is never instatiate in you case.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top