g++ 和 clang++ 在 struct/class 专业化中使用非类型参数的不同行为

g++ and clang++ different behaviour with non-type argument in struct/class specialization

我试图理解 14.5.5/8 of the C++11 standard (idem in C++14 的含义和含义,我想,在 C++17 中)

The type of a template parameter corresponding to a specialized non-type argument shall not be dependent on a parameter of the specialization.

并且像往常一样,了解 g++ 和 clang++ 之间谁是正确的。

标准显示如下示例

template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error

并且 g++ 和 clang++ 都报错。

到目前为止,还不错。

让我们把添加类型的例子复杂一点

template <typename, typename T, T>
struct foo { };

template <typename T>
struct foo<T, int, 1> { }; // compile

template <typename T>
struct foo<T, T, 1> { }; // error

g++ 和 clang++ 都编译了第一个部分特化(1int 的类型不是特化的参数)并给出了第二个部分的错误(1int 的类型1T,特化的一个参数)

到目前为止,还不错。

让我们引入一个模板结构bar,其内部类型依赖于模板参数

template <typename>
struct bar 
 { using type = int; };

和以下程序

template <typename>
struct bar { using type = int; };

template <typename, typename T, T>
struct foo { };

template <typename T>
struct foo<T, typename bar<T>::type, 1> { };

int main ()
 { }

它由 g++ 编译,没有错误(在 4.9.3、5.5.0、7.2.0 和 head 8.0.0 的 wandbox 中试过;用 c++11、c++14 和可用的 c+ +17) 但 clang++ (3.9.1, 4.0.1, 5.0.0, head 6.0.0; c++11, c++14, c++17) 给出以下错误

prog.cc:11:38: error: non-type template argument specializes a template parameter with dependent type 'T'
struct foo<T, typename bar<T>::type, 1> { };
                                     ^
prog.cc:7:34: note: template parameter is declared here
template <typename, typename T, T>
                                ~^

像往常一样:谁是对的?

clang++,认为 1 依赖于 T(当 typename bar<T>::type 固定为 int 时)或 g++ 不解除这种依赖?

为了完整起见,我不得不说更改 bar 如下

template <typename T>
struct bar { using type = T; };

所以让 bar<T>::type 依赖于 T,没有任何变化:g++ 编译没有错误,而 clang++ 给出了同样的错误。

从编译器的角度来看。

template <class T, T t> struct C {};
template <class T> struct C<T, 1>; // error

对于特化,编译器不知道 T 是否确实可以具有 1 的值,因此特化无效。

对于

template <typename T>
struct foo<T, typename bar<T>::type, 1> { };

谁说 type 总是 int?您可能认为这很明显,但我可以为一个特定的 T 引入 bar 的特化,这样 type 就是 std::string:

template<>
struct bar<const volatile int> { using type = std::string };

基本上,你的说法“typename bar<T>::type被固定为int是错误的,它没有固定。

现在呢?这里的标准与您的第一个示例所说的相同,专业化格式错误,因为正如您的引述正确指出的那样,非类型参数的类型取决于专业化的另一个(模板化)类型,即 T,这是未知的。在这方面,clang 是对的,而 gcc 是错的。