这种非类型模板参数扩展如何工作?+ gcc中可能存在的错误?

Vla*_*ovs 5 c++ templates c++11 c++14

template <typename T, typename T::tag...>
void f() {}

struct Tagged { using tag = struct{}; };
struct Untagged {};

void test() {
    f<Tagged>();
    f<Untagged>(); // fails
}
Run Code Online (Sandbox Code Playgroud)

此代码编译在gcc 8.1上,而clang 6.0和MSVC 19失败.请注意,--std=c++14它传递给所有三个.

Clang的错误:

:9:5:错误:调用'f'没有匹配函数

f<Untagged>();

^~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

:2:6:注意:候选模板被忽略:替换失败[T = Untagged]:'Untagged'中没有名为'tag'的类型

void f(){}

 ^
Run Code Online (Sandbox Code Playgroud)

MSVC的错误:

(9):错误C2672:'f':找不到匹配的重载函数

(9):错误C2893:无法专门化函数模板'void f(void)'

(9):注意:使用以下模板参数:

(9):注意:'T =未标记'

(9):注意:'__ formal = {default_valueAttribute}'

但是,如果我们评论有问题的部分:

template <typename T, typename T::tag...>
void f() {}

struct Tagged { using tag = struct{}; };
struct Untagged {};

void test() {
    f<Tagged>();
    //f<Untagged>();
}
Run Code Online (Sandbox Code Playgroud)

它适用于所有三个.现在实际上有两个问题:

1)gcc中的第一个片段被接受了吗?

2)为什么扩展struct代替标签工作?根据C++引用,非类型参数必须是

  • std::nullptr_t (自C++ 11以来);
  • 积分型;
  • 左值引用类型(对象或函数);
  • 指针类型(对象或函数);
  • 指向成员类型的指针(到成员对象或成员函数);
  • 枚举类型.