deadalnix
deadalnix

Reputation: 2325

Function pointer reference parameter conversion to const

The sample code:

class Foo;
typedef void (*fnptr)(Foo &foo);
fnptr gFn;
void myfoo(const Foo &foo) {}

int main() {
    gFn = &myfoo;
}

Fails with the following error using 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 also fails with a similar error. Passing a pointer instead of a reference also

I don't really understand why this is an error. A function accepting a const Foo& also accept a Foo& as argument and, in both cases, a pointer is passed down. I would like to understand why this is an error.

Upvotes: 4

Views: 1096

Answers (2)

Serge Ballesta
Serge Ballesta

Reputation: 148870

The reason is simply because the standard mandates it. Why do we drive on the right in US and on the left in UK? Ok kind of joking, but not that much. There are quite a lot of corner cases like that, where we find ourselves thinking but why didn't they allow this or that? And sometimes what was disallowed in a version of the standard become allowed in a next one. For example in C++98, only static const integral members could be initialized in a class declaration, all other members should be initialized in a constructor. C++11 allows initialization of members in a class declaration.

But here what you ask for would probably make type safety controls harder for compilers, while they are already quite complex!

So the way to go here for a conformant program in to use a wrapper around myfoo:

void myfoo_wrap(Foo &foo) {
    myfoo(foo);
}

I must admit that this may look stupid, but the good news is that an optimizing compiler should be able to remove it from the executable. That means that is is lite in source and not noticiable in executable: I'm afraid that this rule will not be changed soon in standard...

Upvotes: 1

WhiZTiM
WhiZTiM

Reputation: 21576

A function of the signature void(*)(const Foo&) is not the same as void(*)(Foo&).

For example, you can pass rvalues to void myfoo(const Foo&), but you can't to for the void myfoo(Foo&). As, you know, such constraint on what you can pass are checked at compile-time for functions at the call site.

For the sake of sanity, those constraint are also checked on function pointers.

Example:

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
}

Upvotes: 2

Related Questions