Home > Back-end >  c compilation issue with using in a template class
c compilation issue with using in a template class

Time:05-21

I have the following vector class

#include <cmath>
#include <cstdlib>
#include <string>
#include <array>

template<typename T>
class Vec2
{
public:
    Vec2();
    Vec2(T x1, T x2);
    Vec2(const T * data);

    using arr_t = std::array<T,2>;
    Vec2<T>(const arr_t &o) : _x(o) {}

private:
    arr_t _x;
};

It compiles fine in c 20 standard with clang10/linux, but then I have the following error on a Windows MinGW (gcc 11.2) port:

vec2.h:55:17: error: expected unqualified-id before 'const'
55 | Vec2<T>(const arr_t &o) : _x(o) {}
   | ^~~~~

Someone in the comments noticed the extra in the last constructor declaration, which shouldn't be there

Is this a clang bug to accept this? (it compiles and run fine)

CodePudding user response:

Edit

Your code was well-formed per the original C 20 grammar, however there is an accepted defect report in C 20 that disallows a simple-template-id in a constructor declaration.

GCC is ahead of the game here in implementing the DR before the other compilers, it seems. (gcc ref).

The resolution is to change

Vec2<T>(const arr_t &o) : _x(o) {}

into

Vec2(const arr_t &o) : _x(o) {}

Original answer

Your code is well-formed per the C grammar.

Per [class.ctor.general], a class constructor that is defined within the class declaration should be an "id-expression" that consists of the "injected class name", which in most cases is meant to simply refer to the name of the class sans any template arguments (formally, an identifier instead of a simple-template-id).

However, the C grammar defines class name as being either a plain identifier (Vec2) or a simple-template-id (Vec2<T>) ([class.pre]).

I will note that clang (trunk) and MSVC (latest) both accept your code, while gcc (trunk) fails. I believe this is a gcc bug.

CodePudding user response:

The shown snippet is valid for Pre-C 20 but not valid from C 20 & onwards as explained below:

Pre-C 20

From class.ctor#1.2:

1 -- Constructors do not have names. In a declaration of a constructor, the declarator is a function declarator of the form:
ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

1.2 -- in a member-declaration that belongs to the member-specification of a class template but is not a friend declaration, the id-expression is a class-name that names the current instantiation of the immediately-enclosing class template; or

(end quote)

This means that in C 17, we are allowed to use Vec2<T>(const arr_t &o); as the constructor declaration.

C 20

From class.ctor#1.1:

1 -- A constructor is introduced by a declaration whose declarator is a function declarator ([dcl.fct]) of the form:
ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

1.1 -- in a member-declaration that belongs to the member-specification of a class or class template but is not a friend declaration ([class.friend]), the id-expression is the injected-class-name ([class.pre]) of the immediately-enclosing entity or

So as we can see, the injected class name(which is Vec2 and not Vec2<T> in your example) is needed for declaring the ctor of a class template.

This means that your given code is not valid for C 20.


The same is also mentioned at diff.cpp17.class#2:

Affected subclauses: [class.ctor] and [class.dtor]

Change: A simple-template-id is no longer valid as the declarator-id of a constructor or destructor.

Rationale: Remove potentially error-prone option for redundancy.

Effect on original feature: Valid C 2017 code may fail to compile in this revision of C . For example:

template<class T>
struct A {
  A<T>();            // error: simple-template-id not allowed for constructor
  A(int);            // OK, injected-class-name used
  ~A<T>();           // error: simple-template-id not allowed for destructor
};
  • Related