具有非类型参数的部分模板特化:GCC 与 MSVS
Partial template specialization with non-type parameters: GCC vs MSVS
考虑这个简单的模板专业化:
template<typename T, size_t I>
struct S {};
template<typename T>
struct S<T, std::tuple_size<T>::value> {};
GCC 不编译它,因为它在模板参数 std::tuple_size<T>::value
:
中使用模板参数 T
error: template argument 'std::tuple_size<_Tp>::value'
involves template parameter(s)
现在让我们在 tuple_size
模板参数中用 typename std::remove_reference<T>::type
替换 T
:
// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};
此代码在模板参数中仍然使用模板参数,但 GCC 编译它时没有任何错误或警告。为什么?
现在,如果我们尝试使用带有 /std:c++latest
标志的 MSVS 编译第二个示例,它将停止并出现错误 C2755:
non-type parameter of a partial specialization must be a simple
identifier
这是什么奇怪的限制?当 I
等于元组大小时,我想停止编译时递归。
那么他们谁错了:MSVS 还是 GCC?
请注意,即使没有任何模板实例化,MSVS 也会报告错误,而 GCC 可以很好地处理所有这些实例:
S<std::tuple<int, float>, 9> s1;
S<std::tuple<int, float>, 2> s2;
S<int, 42> s3;
我使用 MSVS Community 2015 Update 3 及其默认编译器和 GCC 6.2.1。
尝试了 Clang 3.8.0。它不会编译两个片段,并出现类似于 GCC 消息的错误:
error: non-type template argument depends on a template parameter of
the partial specialization
处理部分 class 模板专业化可行性的标准的特定部分在过去几年中已更改多次。 [temp.class.spec.match] 中的原始限制为:
A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.
您的代码显然与此冲突,std::tuple_size<T>::value
不是标识符。
然后在 cwg issue 1315 之后更改为:
Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
但我们没问题 - T
在 non-deduced 上下文中用作第一个模板参数。在 template auto
之后,它现在显示为:
If the template arguments of a partial specialization cannot be deduced because of the structure of its
template-parameter-list and the template-id, the program is ill-formed.
但我们在那里也很好。可以推断,您有正确的 "structure" - 您的专业化在与主要相同的位置使用 non-type 模板参数,它们应该匹配得很好。
我觉得按照1315的解析(我觉得是post-C++14),代码应该是well-formed,但是gcc 和 clang 都拒绝它。一个不幸的修复是使用两个 type 参数来代替:
template <class T, class I>
struct S;
template<typename T>
struct S<T, typename std::tuple_size<T>::type> {};
template <size_t I>
using size_ = std::integral_constant<size_t, I>;
int main() {
S<std::tuple<int>, size_<1>> s;
}
gcc 和 clang 都接受那个。
考虑这个简单的模板专业化:
template<typename T, size_t I>
struct S {};
template<typename T>
struct S<T, std::tuple_size<T>::value> {};
GCC 不编译它,因为它在模板参数 std::tuple_size<T>::value
:
T
error: template argument 'std::tuple_size<_Tp>::value' involves template parameter(s)
现在让我们在 tuple_size
模板参数中用 typename std::remove_reference<T>::type
替换 T
:
// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};
此代码在模板参数中仍然使用模板参数,但 GCC 编译它时没有任何错误或警告。为什么?
现在,如果我们尝试使用带有 /std:c++latest
标志的 MSVS 编译第二个示例,它将停止并出现错误 C2755:
non-type parameter of a partial specialization must be a simple identifier
这是什么奇怪的限制?当 I
等于元组大小时,我想停止编译时递归。
那么他们谁错了:MSVS 还是 GCC?
请注意,即使没有任何模板实例化,MSVS 也会报告错误,而 GCC 可以很好地处理所有这些实例:
S<std::tuple<int, float>, 9> s1;
S<std::tuple<int, float>, 2> s2;
S<int, 42> s3;
我使用 MSVS Community 2015 Update 3 及其默认编译器和 GCC 6.2.1。
尝试了 Clang 3.8.0。它不会编译两个片段,并出现类似于 GCC 消息的错误:
error: non-type template argument depends on a template parameter of the partial specialization
处理部分 class 模板专业化可行性的标准的特定部分在过去几年中已更改多次。 [temp.class.spec.match] 中的原始限制为:
A partially specialized non-type argument expression shall not involve a template parameter of the partial specialization except when the argument expression is a simple identifier.
您的代码显然与此冲突,std::tuple_size<T>::value
不是标识符。
然后在 cwg issue 1315 之后更改为:
Each template-parameter shall appear at least once in the template-id outside a non-deduced context.
但我们没问题 - T
在 non-deduced 上下文中用作第一个模板参数。在 template auto
之后,它现在显示为:
If the template arguments of a partial specialization cannot be deduced because of the structure of its template-parameter-list and the template-id, the program is ill-formed.
但我们在那里也很好。可以推断,您有正确的 "structure" - 您的专业化在与主要相同的位置使用 non-type 模板参数,它们应该匹配得很好。
我觉得按照1315的解析(我觉得是post-C++14),代码应该是well-formed,但是gcc 和 clang 都拒绝它。一个不幸的修复是使用两个 type 参数来代替:
template <class T, class I>
struct S;
template<typename T>
struct S<T, typename std::tuple_size<T>::type> {};
template <size_t I>
using size_ = std::integral_constant<size_t, I>;
int main() {
S<std::tuple<int>, size_<1>> s;
}
gcc 和 clang 都接受那个。