我想声明嵌套模板类,但在类定义之外定义最内部类的构造函数和成员函数(出于我正在开发的项目的特定原因,但这主要是为了大规模减少编译时间和内存).
一些编译器以不同的方式解析类定义,并且似乎遵循不同的标准。特别是,c ++ 20标准模式中的g ++表示构造函数的外部定义中的类标识符是一个依赖名称,必须在前面加上
typename
关键字,这对我来说很奇怪,因为它是一个类标识符,而不是一个此时输入。奇怪的是,对于外部成员函数定义,编译器的行为是不同的,为此,g++ 在定义成员函数代码时不会抱怨类标识符的相同使用。
使 g++ 在 c++20 模式下快乐,反过来又可以防止在 msvc 上编译代码。 我想了解标准并找到嵌套模板类构造函数的可靠外部定义。
以下代码可以在 c++-17 模式下的 g++ 上编译良好,也可以在 c++-20 模式下的 clang 和 msvc 上编译良好,但不能在 c++-20 模式下的 g++ 上编译:
#include <iostream>
template <typename T> struct A {
struct B {
template <typename TT> struct C {
C();
void foo();
};
};
};
template <typename T>
template <typename TT>
A<T>::B::C<TT>::C() {
std::cout << "constructor" << std::endl;
}
template <typename T>
template <typename TT>
void A<T>::B::C<TT>::foo() {
std::cout << "foo" << std::endl;
}
int main()
{
A<int>::B::C<int> c;
c.foo();
}
奇怪的是,g++ 可以解析类外
A<T>::B::C<TT>::foo()
的定义,却不能解析构造函数A<T>::B::C<TT>::C()
的定义。
它返回以下编译错误:
<source>:15:10: error: non-template 'C' used as template
15 | A<T>::B::C<TT>::C() {
| ^
<source>:15:10: note: use 'typename A<T>::B::template C' to indicate that it is a template
<source>:15:1: error: need 'typename' before 'A<T>::B::C' because 'A<T>::B' is a dependent scope
15 | A<T>::B::C<TT>::C() {
| ^~~~
| typename
Compiler returned: 1
但是,如果我通过以下行更改上面构造函数的定义:
template <typename T>
template <typename TT>
A<T>::B::template C<TT>::C() {
std::cout << "constructor" << std::endl;
}
然后它可以在 c++17 和 c++20 模式下使用 g++ 和 clang 进行编译,但不能使用 msvc。请注意,由于某种原因,
void A<T>::B::C<TT>::foo()
的外部定义不需要更改。
关于标准,哪个编译器是正确的?如何使此代码在 C++20 模式下对 g++、clang 和 msvc 解析器具有鲁棒性?
如何使此代码对 C++20 模式下的 g++、clang 和 msvc 解析器具有鲁棒性?
在为 ctor 和成员函数提供类外定义时,需要使用
template
关键字(用于依赖模板名称 C
),如下所示:
//other code as before
template <typename T>
template <typename TT>
//-------vvvvvvvv--------------------->use template keyword as dependent template name
A<T>::B::template C<TT>::C() {
std::cout << "constructor" << std::endl;
}
template <typename T>
template <typename TT>
//------------vvvvvvvv-------------------->use template keyword as dependent template name
void A<T>::B::template C<TT>::foo() {
std::cout << "foo" << std::endl;
}
请注意,msvc 在 c++17 和 c++20 中都拒绝上述代码,这是一个 msvc bug。