函数指针引用参数转换为const

时间:2017-05-31 13:49:21

标签: c++

示例代码:

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&作为参数,在两种情况下,指针都向下传递。我想了解为什么这是一个错误。

2 个答案:

答案 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);
}

我必须承认这可能看起来很愚蠢,但好消息是优化编译器应该能够从可执行文件中删除它。这意味着它在源代码中是精简版而在可执行文件中并不明显:我担心此规则不会很快在标准中更改...