为什么C++-20模式下的g++不识别类外与成员函数相反的嵌套模板类构造函数定义?

问题描述 投票:0回答:1

我想声明嵌套模板类,但在类定义之外定义最内部类的构造函数和成员函数(出于我正在开发的项目的特定原因,但这主要是为了大规模减少编译时间和内存).

一些编译器以不同的方式解析类定义,并且似乎遵循不同的标准。特别是,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++ templates g++ c++20
1个回答
0
投票

如何使此代码对 C++20 模式下的 g++、clang 和 msvc 解析器具有鲁棒性?

在为 ctor 和成员函数提供类外定义时,需要使用

template
关键字,如下所示:

//other code as before
template <typename T>
template <typename TT>
//-------vvvvvvvv--------------------->use template keyword as dependent context
A<T>::B::template C<TT>::C() {   
    std::cout << "constructor" << std::endl;
}

template <typename T>
template <typename TT>
//------------vvvvvvvv-------------------->use template keyword as dependent context
void A<T>::B::template C<TT>::foo() {  
    std::cout << "foo" << std::endl;
}

工作演示


请注意,msvc 在 c++17 和 c++20 中都拒绝上述代码,这是一个 msvc bug。

© www.soinside.com 2019 - 2024. All rights reserved.