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
149 views
in Technique[技术] by (71.8m points)

c++ - Is it required to explicitly list default parameters when using template template parameter?

I'd like to ask whether the following code sample should compile:

#include <iostream>
#include <vector>
#include <typeinfo>

using namespace std;

template <template <class...> class C>
struct convert_container
{
    using type = C<double>; 

    // Visual Studio requires this to be:
    // using type = C<double, std::allocator<doble>>
};

int main()
{
    std::cout << typeid(convert_container<std::vector>::type).name();
}

The code compiles fine with GCC 4.8.1 and Clang 3.4 but not with Visual Studio 2013. The error I get:

error C2976: 'std::vector' : too few template arguments
    c:program files (x86)microsoft visual studio 12.0vcincludevector(650) : see declaration of 'std::vector'
    c:usersmicha?documentsvisual studio 2013projectsransformransform.cpp(14) : see reference to class template instantiation 'convert_container<std::vector>' being compiled

What does the standard say about this? Am I required to explicitly state all the parameters (including defaulted ones) when using the template template parameter C or is this just a bug in VC++?

Context: The issue araised from Constructor's answer to my previous question: https://stackoverflow.com/a/23874768/2617356

When searching the archives I've found this question: Default values in templates with template arguments ( C++ ) It's basically about the same problem, the question author states that default parameters for template template parameter "had to be" explicitly stated. However, the asker accepted solution that's not quite applicable in my case. The question was not about what is the standard-conforming behaviour, so I believe this is not a duplicate.

See Question&Answers more detail:os

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

1 Reply

0 votes
by (71.8m points)

Consider the similar

template <typename = void, int = 0> struct A { };
template <template <typename ...> class T> struct B : T<> { };
template class B<A>;

This is clearly covered by the standard (14.3.3p3 if you're interested, I won't quote it, as GCC and clang do both implement the rule already), where the use of A as a template argument for B is disallowed because of the non-type template parameter. That rule makes no sense if the instantiation of a template template parameter could make use of the template template argument's default template arguments, so the behaviour of MSVC and Intel is more consistent than that of GCC and clang.

Of course, the reasoning "if this were valid, the standard would have inconsistencies" doesn't actually mean it isn't valid, only that it shouldn't be valid. To actually check what the standard says:

14.1 Template parameters [temp.param]

10 The set of default template-arguments available for use with a template declaration or definition is obtained by merging the default arguments from the definition (if in scope) and all declarations in scope in the same way default function arguments are (8.3.6).

8.3.6 Default arguments [dcl.fct.default]

4 Declarations in different scopes have completely distinct sets of default arguments. That is, declarations in inner scopes do not acquire default arguments from declarations in outer scopes, and vice versa.

Although not specifically intended to address this use of default template arguments, I think it does manage to do so. Nikos Athanasiou has already included the part of the standard that says any default template arguments of C do get used:

14.1 Template parameters [temp.param]

14 A template-parameter of a template template-parameter is permitted to have a default template-argument. When such default arguments are specified, they apply to the template template-parameter in the scope of the template template-parameter.

Since C's default template arguments are used, std::vector's aren't, and MSVC and Intel seem to be correct here.

And to come up with an example that clearly shows that GCC and clang cannot be considered to conform here:

template <typename = char, typename = short>
struct A { };

template <template <typename = void, typename ...> class T>
struct B {
  using type = T<>;
};

Both GCC and clang treat B<A>::type as A<void, short>, taking one default template argument from T, and another from A, even though the standard disallows merging of default arguments (and hence default template arguments) in declarations in different scopes.


A workaround for you, to avoid the need to type out the allocator argument, could be to use a template alias:

template <template <class...> class C>
struct convert_container
{
  using type = C<double>; 
};

template <typename T>
using vector_default_alloc = std::vector<T>;

int main()
{
  std::cout << typeid(convert_container<vector_default_alloc>::type).name();
}

I cannot test on MSVC right now, but Intel accepts it, and I see no reason why this variant would be invalid.


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

...