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.