示例代码:
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&作为参数,在两种情况下,指针都向下传递。我想了解为什么这是一个错误。
答案 0 :(得分:2)
签名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
}
答案 1 :(得分:1)
原因只是,因为标准要求。为什么我们在美国和英国的左边开车?好开玩笑,但不是那么多。有很多像这样的角落案例,我们发现自己在思考,但为什么他们不允许这样或那样?有时在下一个版本中允许在标准版本中禁止的内容。例如,在C ++ 98中,只能在类声明中初始化静态const积分成员,所有其他成员都应该在构造函数中初始化。 C ++ 11允许在类声明中初始化成员。
但是在这里你要求的可能会使编译器的类型安全控制更难,而它们已经非常复杂了!
因此,在myfoo
周围使用包装器的方法是一致的程序:
void myfoo_wrap(Foo &foo) {
myfoo(foo);
}
我必须承认这可能看起来很愚蠢,但好消息是优化编译器应该能够从可执行文件中删除它。这意味着它在源代码中是精简版而在可执行文件中并不明显:我担心此规则不会很快在标准中更改...