Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does g++ in C++-20 mode not recognize nested template class constructor definitions outside the class contrary to member functions?

I would like to declare nested template classes but define the constructor and member functions of the most inner class outside the class definition (for reasons which are specific to a project I am working on, but it's mainly to reduce compilation time and memory at scale).

Some compilers parse the class definition differently and seem to follow different standards. Especially, g++ in c++20 standard mode says that the class identifier in the external definition of the constructor is a dependent name that must be preceded by the typename keyword, which seems weird to me since it's a class identifier, not a type at this point. Strangely, the compiler behaviour is different for external member function definitions, for which g++ does not complain about the same use of the class identifier when defining the member function code.

Making g++ in c++20 mode happy prevents in return from compiling the code on msvc. I would like to understand the standards and to find a robust external definition of nested template class constructors.

The following code compiles fine on g++ in c++-17 mode, as well on clang and msvc in c++-20 mode, but not with g++ in c++-20 mode:

#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();
}

Strangely, g++ can parse the definition of A<T>::B::C<TT>::foo() outside the class, but not the definition of the constructor A<T>::B::C<TT>::C(). It returns the following compilation error:

<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

However, if I change the definition of the constructor above by the following lines:

template <typename T>
template <typename TT>
A<T>::B::template C<TT>::C() {
    std::cout << "constructor" << std::endl;
}

then it compiles with g++ and clang in both c++17 and c++20 modes but not with msvc. Note that the external definition of void A<T>::B::C<TT>::foo() does not need to be changed for some reason.

Which compiler is right regarding the standards? How to make this code robust to g++, clang and msvc parsers in C++20 mode?

like image 872
fteicht Avatar asked Dec 06 '25 13:12

fteicht


1 Answers

How to make this code robust to g++, clang and msvc parsers in C++20 mode?

You need to use template keyword(for the dependent template name C) when providing the out of class definition for the ctor and the member function as shown below:

//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;
}

Working demo


Note that msvc rejects the above code in both c++17 and c++20 which is a msvc bug.

like image 169
Anoop Rana Avatar answered Dec 08 '25 04:12

Anoop Rana



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!