我注意到函数声明中存在的值参数可以有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);
}
当我使用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);
^
GCC在定义中优先考虑限定词。 Clang使用了声明,仅用于模板类A
。
我的问题是:
const
限定符在声明和定义之间使用不一致?更新
根据评论,这似乎是一个Clang bug。我打开了new ticket。
更新
错误已修复:
已在r203741中修复
答案 0 :(得分:12)
如果我们查看draft C++ standard部分gcc
可重载声明13.1在这里是正确的。 > 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 ]
以及一些细节,澄清这仅适用于最外层的 cv限定符(强调我的):
以这种方式只忽略参数类型规范最外层的const和volatile类型说明符;掩埋在参数类型规范中的 const和volatile类型说明符很重要,可用于区分重载的函数声明。 123 特别是对于任何类型T,“指向T,“”指向const T的指针“和”指向volatile T的指针“被认为是不同的参数类型,”对T的引用“,”对const T的引用“和”引用到 不稳定的T。“
据我所知,这适用于模板类中的模板函数以及14.8
部分函数模板特化特别是14.8.3
重载决议其中说:
[...]完整的候选函数集包括所有合成的声明和所有同名的非模板重载函数。除非在13.3.3中明确指出,否则合成声明将被视为重载决策剩余部分中的任何其他函数。 144
答案 1 :(得分:2)
这是一个错误,因为它会阻止合法代码,如:
/* API declaration */
void f(int);
/* Implementation */
void f(const int x) /* my business: x is my local var and I want it const */
{
}
我无法相信任何人都会用自己的方式将其诊断为问题。
顺便说一句,海湾合作委员会(GCC)并没有对此抱怨,过去常常对这种情况发出警告。也许它仍然存在:
void f(int func_ptr(void));
void f(int (*func_ptr)(void))
{
}
这纯粹是一种风格上的不一致,但并不能达到目的。