声明和定义之间使用const限定符不一致

det*_*zed 15 c++ gcc clang

我注意到可以const在函数声明中出现值参数的限定符,然后在定义中省略.这不会改变函数的签名.它实际上编译得很好.

我还注意到常规类和模板类之间的行为是不同的.GCC和Clang的处理方式也有区别.

请考虑以下代码:

template <typename T> struct A {
    void f(const int);
};

template <typename T> void A<T>::f(int x) {
    x = 0;
}

struct B {
    void f(const int);
};

void B::f(int x) {
    x = 0;
}

void f() {
    A<float> a;
    a.f(0);

    B b;
    b.f(0);
}
Run Code Online (Sandbox Code Playgroud)

当我使用GCC编译时,我没有错误.有了Clang我得到:

test.cpp:10:7: error: read-only variable is not assignable
    x = 0;
    ~ ^
test.cpp:26:7: note: in instantiation of member function 'A<float>::f' requested here
    a.f(0);
      ^
Run Code Online (Sandbox Code Playgroud)

海湾合作委员会在定义中优先考虑限定词.Clang使用了声明,仅用于模板类A.

我的问题是:

  1. 这是由标准规定还是定义了这个实现?
  2. 为什么常规类和模板类之间的行为不同?
  3. 为什么const在声明和定义之间不一致地使用限定符时没有错误或至少有警告?
  4. 是否有任何可能有用的情况?

更新:

根据评论,它似乎是一个Clang bug.我开了一张新票.

更新:

错误是固定的:

已在r203741中修复

Sha*_*our 12

这个行为是由标准定义的,据我所知gcc在这里是正确的,如果我们看一下草案C++标准部分13.1 Overloadable声明3段说:

[...] - 仅在存在或不存在const和/或volatile时不同的参数声明是等效的.也就是说,在确定声明,定义或调用哪个函数时,将忽略每个参数类型的const和volatile类型说明符.

并提供此示例:

[ Example:
    typedef const int cInt;

    int f (int);
    int f (const int); // redeclaration of f(int)
    int f (int) { /* ... */ } // definition of f(int)
    int f (cInt) { /* ... */ } // error: redefinition of f(int)
—end example ]
Run Code Online (Sandbox Code Playgroud)

以及一些细节澄清,这仅适用于最外层的cv限定符(强调我的):

仅以这种方式忽略参数类型规范最外层的const和volatile类型说明符; 埋在参数类型规范中的const和volatile类型说明符很重要,可用于区分重载的函数声明.123特别是,对于任何类型T,"指向T的指针","指向const T的指针"和"指向易失性T的指针"被认为是不同的参数类型,"对T的引用","对const T的引用"也是如此.和"参考挥发性T".

并且据我所知,这适用于模板类中的模板函数以及14.8 功能模板特化部分,特别是14.8.3 重载分辨率,它说:

[...]完整的候选函数集包括所有合成的声明和所有同名的非模板重载函数.除非在13.3.3中明确指出,否则合成声明将被视为重载决策的其余部分中的任何其他函数.144