Home > database >  Variadic template template wrapper: weird compilers errors, possibly bugs
Variadic template template wrapper: weird compilers errors, possibly bugs

Time:05-09

Over years of template metaprogramming practice, I have encountered all sorts of weird compiler bugs and errors. But with this one, I must say that I am somewhat puzzled. I have no idea which compiler is correct: gcc, clang, msvc, and intel all give different results (and as surprising as it may sound, only intel compiles the code without errors). Even more surprising, it does not rely on any new C feature as only C 11 is involved.


The code will speak for itself as it is relatively simple. It only consists of a variadic template template wrapper with a variadic inner entity that can be either a struct or an alias template. And for whatever reason, the alias template version returns an error for some compilers when an instance of it is constructed:

#include <type_traits>

template <class T1, class T2, class T3> 
struct ternary {};

template <template <class...> class Template, class... Types>
struct template_struct {
    template <class... Args>
    struct type: Template<Types..., Args...> {};
};

template <template <class...> class Template, class... Types>
struct template_alias {
    template <class... Args>
    using type = Template<Types..., Args...>;
};

And now the test:

int main(int, char**) {
    using ts0 = template_struct<ternary>;                           // OK
    using ts1 = template_struct<ternary, bool>;                     // OK
    using ts2 = template_struct<ternary, bool, char>;               // OK
    using ts3 = template_struct<ternary, bool, char, int>;          // OK
    using ts4 = template_struct<ternary, bool, char, int, double>;  // OK
    ts0 s0;     // OK
    ts1 s1;     // OK
    ts2 s2;     // OK
    ts3 s3;     // OK    
    ts4 s4;     // OK
    using ta0 = template_alias<ternary>;                            // OK
    using ta1 = template_alias<ternary, bool>;                      // OK
    using ta2 = template_alias<ternary, bool, char>;                // OK
    using ta3 = template_alias<ternary, bool, char, int>;           // OK
    using ta4 = template_alias<ternary, bool, char, int, double>;   // OK
    ta0 a0;     // OK
    ta1 a1;     // OK
    ta2 a2;     // OK
    ta3 a3;     // GCC, INTEL => OK | CLANG, MSVC => ERROR: WAIT WHAT ?!?!
    ta4 a4;     // INTEL => OK | GCC, CLANG, MSVC => ERROR
    return 0;
}

The code is available on compiler explorer: https://godbolt.org/z/3ndYMWvfs


QUESTION: What is happening? Which compiler is correct? What does the standard say? Is it a compiler bug?

CodePudding user response:

All compilers are right, the two failing tests are ill-formed NDR ("No Diagnostic Required").

The > 3 arguments case is ill-formed NDR because of [temp.res.general]/6.1:

The program is ill-formed, no diagnostic required, if:

— no valid specialization can be generated for a template ... and the template is not instantiated, ...

The == 3 arguments case is ill-formed NDR because of [temp.res.general]/6.3:

The program is ill-formed, no diagnostic required, if:

— every valid specialization of a variadic template requires an empty template parameter pack ...

The two template_struct<ternary, ...> tests appear to be legal because you can specialize the struct type into something valid, and I don't immediately see a rule that there must be at least one valid specialization of the primary (unspecialized) template.

  • Related