以下代码:
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;
}
在名称空间Foo
中涉及类模板N
的(转发)声明
具有两个类型参数(A
和B
)。 (向前)声明为第二个参数提供了一个与第一个参数(A
)相同的默认值。稍后,使用不同名称的参数(C
和D
)定义此类。由于已经指定,因此未提供默认值。
这会在GCC 7.1和Clang 3.8(在C ++ 17模式下)中进行编译,只是会警告未使用的变量foo
。但是,VS 2017 15.7.5(在C ++ 17模式下)说A
是未定义的标识符,并且说它不是参数D
的有效类型名称。
令我惊讶的是,当我将代码更改为此:
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接受它! (不过,它不能在GCC或Clang中编译)
我认为VS在这里是错误的。它尝试在不同的上下文中解释默认参数(在这种特定情况下,Foo
的定义),而GCC和Clang则不然。我在哪一步错了?还是这是VS的错误?
任何帮助将不胜感激。
(对不起,我的英语不好)
答案 0 :(得分:2)
我会说这绝对是编译器错误。在后继参数的默认参数中使用前一个模板参数的名称是常见的做法,标准库本身会在所有位置执行此操作(例如,标准容器的分配器和比较函数)。该标准甚至在[basic.scope.temp] §3的注释中明确提到了这种可能性。
VS 2015似乎也受到了影响。请注意,模板甚至不必位于另一个名称空间中。我进一步简化了您的示例:
template <typename A, typename B = A>
struct S;
template <typename C, typename D>
struct S {};
int main()
{
S<int> s;
}
这似乎已经触发了问题……
答案 1 :(得分:-1)
尝试一下。
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;
}
您可以在网页中查看原因:https://docs.microsoft.com/en-us/cpp/cpp/templates-cpp
T是模板参数; typename关键字表示 参数是类型的占位符。调用该函数时, 编译器将 T的每个实例替换为具体类型 由用户指定或由推断的参数 编译器。
例如,如果您编写如下代码,则必须是错误:
double example(double,int);
int main()
{
//...
}
double example(double a,double b)
{
//...
}
因为您指定的TYPE与原型冲突。
(对不起我的英语不好)