示例代码:
class Foo;
typedef void (*fnptr)(Foo &foo);
fnptr gFn;
void myfoo(const Foo &foo) {}
int main() {
gFn = &myfoo;
}
使用clang的错误失败:
main.cpp:9:9: error: assigning to 'fnptr' (aka 'void (*)(Foo &)') from incompatible type
'void (*)(const Foo &)': type mismatch at 1st parameter ('Foo &' vs 'const Foo &')
gFn = &myfoo;
^ ~~~~~~
1 error generated.
GCC也出于类似的错误而失败。传递指针而不是参考
我不太了解为什么这是一个错误。接受const foo&也接受foo&作为参数,在这两种情况下,指针都被传递了。我想了解为什么这是一个错误。
签名 void(*)(const Foo&)
的函数是与 void(*)(Foo&)
相同。
例如,您可以将rvalues
传递给void myfoo(const Foo&)
,但是您不能使用void myfoo(Foo&)
。因为,您知道,可以在呼叫站点的函数上检查可以通过的限制。
为了理智,还检查了这些约束。
示例:
class Foo{};
using FooPtr = void(*)(Foo&);
using ConstFooPtr = void(*)(const Foo&);
void fooNonConst(Foo&) { }
void fooConst(const Foo&) { }
int main() {
FooPtr ptr1 = &fooNonConst;
ConstFooPtr ptr2 = &fooConst;
ptr1(Foo{}); //Not OK
ptr2(Foo{}); //Ok
}
原因只是,因为标准要求它。为什么我们在我们的右边和英国的左边开车?好的开玩笑,但没有那么多。这样的角落有很多,我们发现自己在思考,但是为什么他们不允许这样做呢?有时,在下一个标准版本中不允许使用的内容。例如,在C 98中,只能在类声明中初始化静态常量积分成员,所有其他成员都应在构造函数中初始化。C 11允许在类声明中初始化成员。
但是,您所要求的可能会使编译器的类型安全控制更难,而它们已经很复杂了!
因此,在此处使用myfoo
围绕包装器的一致程序的方法:
void myfoo_wrap(Foo &foo) {
myfoo(foo);
}
我必须承认这看起来可能很愚蠢,但是好消息是优化编译器应该能够从可执行文件中删除它。这意味着这在源中是精简版,并且在可执行文件中不明显:恐怕该规则不会在标准中很快更改...