Reputation: 20649
The following code:
namespace N {
template <typename A, typename B = A>
class Foo;
}
template <typename C, typename D>
class N::Foo {
public:
Foo() = default;
};
int main()
{
N::Foo<char> foo;
}
involves a (forward) declaration of the class template Foo
inside namespace N
with two type parameters (A
and B
). The (forward) declaration gives the second parameter a default value which is identical to the first parameter (A
). Later, this class is defined with its parameters differently named (C
and D
). Since it is already specified, the default is not given.
This compiles in GCC 7.1 and Clang 3.8 (in C++17 mode) except that the unused variable foo
is warned. However, VS 2017 15.7.5 (in C++17 mode) says A
is undefined identifier, and says it is not a valid type name for parameter D
.
To my surprise, when I changed the code into this:
namespace N {
template <typename A, typename B = C> //note: A changed into C
class Foo;
}
template <typename C, typename D>
class N::Foo {
public:
Foo() = default;
};
int main()
{
N::Foo<char> foo;
}
VS accepts it! (It does not compile in GCC or Clang, though)
I think VS is wrong here. It tries to interpret the default argument in a different context (in this specific case, the definition of Foo
) whereas GCC and Clang don't. At which step was I wrong? Or is this a bug of VS?
Any help will be appreciated.
(Sorry for my bad English)
Upvotes: 2
Views: 133
Reputation: 1
Try this.
namespace N {
template <typename A, typename B = A>
class Foo;
}
template <typename A, typename B> //A and B is TYPE
class N::Foo {
public:
Foo() = default;
};
int main()
{
N::Foo<char> foo;
}
You can check the reason in the webpage:https://learn.microsoft.com/en-us/cpp/cpp/templates-cpp
T is a template parameter; the typename keyword says that this parameter is a placeholder for a type. When the function is called, the compiler will replace every instance of T with the concrete type argument that is either specified by the user or deduced by the compiler.
For example, it must be an error if you write a code like:
double example(double,int);
int main()
{
//...
}
double example(double a,double b)
{
//...
}
because the TYPE you specified conflicts with the prototype.
(Sorry for my poor English)
Upvotes: -1
Reputation: 15951
I would say that this is definitely a compiler bug. Using the name of a previous template parameter in the default argument of a subsequent parameter is common practice, the standard library itself does it all over the place (e.g., allocators and comparison functions for standard containers). The standard even explicitly mentions this possibility in a note in [basic.scope.temp] §3.
VS 2015 seems to be affected too. Note that the template doesn't even have to be in another namespace. I stripped down your example a bit further:
template <typename A, typename B = A>
struct S;
template <typename C, typename D>
struct S {};
int main()
{
S<int> s;
}
and this seems to already trigger the issue…
Upvotes: 2