Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
547 views
in Technique[技术] by (71.8m points)

c++ - Partial template specialization with non-type parameters: GCC vs MSVS

Consider this simple template specialization:

template<typename T, size_t I>
struct S {};

template<typename T>
struct S<T, std::tuple_size<T>::value> {};

GCC does not compile it, as it uses template parameter T in the template argument std::tuple_size<T>::value:

error: template argument 'std::tuple_size<_Tp>::value' involves template parameter(s)

Now let's replace T with typename std::remove_reference<T>::type in tuple_size template argument:

// Using primary structure template from previous example.
template<typename T>
struct S<T, std::tuple_size<typename std::remove_reference<T>::type>::value> {};

This code still uses template parameter in template argument, but GCC compiles it without any errors or warnings. Why?

Now if we try to compile the second example using MSVS with /std:c++latest flag, it stops with error C2755:

non-type parameter of a partial specialization must be a simple identifier

What is this strange restriction? I want to stop compile-time recursion when I becomes equal to tuple size.

So who of them is wrong: MSVS or GCC?

Note that MSVS reports the error even without any template instantiations, while GCC works fine with all of these instances:

S<std::tuple<int, float>, 9> s1;
S<std::tuple<int, float>, 2> s2;
S<int, 42> s3;

I use MSVS Community 2015 Update 3 with it's default compiler and GCC 6.2.1.

Tried Clang 3.8.0. It does not compile both snippets with an error similar to GCC's message:

error: non-type template argument depends on a template parameter of the partial specialization

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The specific section of the standard dealing with the viability of a partial class template specialization has been changed lots of times over the last few years. The original restrictionin [temp.class.spec.match] read:

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.

Your code clearly runs afoul of that, std::tuple_size<T>::value is not an identifier.

It then changed, after cwg issue 1315, to read:

Each template-parameter shall appear at least once in the template-id outside a non-deduced context.

But we're okay there - T is used in a non-deduced context as the first template parameter. And after template auto, it now reads:

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.

But we're okay there too. It can be deduced, you have the right "structure" - your specialization is using a non-type template parameter in the same location as the primary, and they should match fine.


I think following the resolution of 1315 (which I think is post-C++14), the code should be well-formed, but both gcc and clang reject it. An unfortunate fix would be to use two type parameters instead:

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;
}

Both gcc and clang accept that one.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...