2

I am trying to understand more Class-type conversion. I am reading C++ primer 5ed. So I've tried this code:

int add(int x, int y) { return x + y;}

struct Foo
{
    //operator(int(*)(int, int))(){return add;} // error
    using pfn = int(*)(int, int);
    operator pfn(){return add;} // OK
    double value = 5.45;
};

int main()
{

    cout << (int(*)(int, int))Foo()(5, 7) << endl; // why 1
    cout << ((int(*)(int, int))Foo())(5, 7) << endl; // Ok 12

    std::cout << "\nDone!\n";
}
  • So why I cannot directly define conversion for my class using the type int(*)(int, int) but I can with a type alias?

  • Why I get value 1 in the first statement which is erroneous and get it correct in the second statement using parenthesis?

  • I get the warning from first statement: Description Resource Path Location Type cast to pointer from integer of different size [-Wint-to-pointer-cast] main.cpp /MyCppProj line 31 C/C++ Problem

Maestro
  • 2,512
  • 9
  • 24
  • 3
    The first one casts `Foo()(5, 7)` – which is 12 – to a function pointer; non-null pointers convert to `true`, which is printed as `1`. – molbdnilo May 04 '20 at 10:14
  • 1
    I'm still struggling with the correct syntax for the operator. However, for a member function would look like this: `int (*getAdd())(int, int) { return add; }`: [**demo on coliru**](http://coliru.stacked-crooked.com/a/94dff61076298e38) I'm not really sure that the operator can be defined at all. I believe your alternative with `using` isn't that bad. (At least, it's much more readable.) – Scheff's Cat May 04 '20 at 10:22
  • After having got serious doubts that it's possible at all, I googled and found: [SO: C++ Conversion operator for converting to function pointer](https://stackoverflow.com/a/6755760/7478597). – Scheff's Cat May 04 '20 at 10:28
  • 1
    Does this answer your question? [C++ Conversion operator for converting to function pointer](https://stackoverflow.com/questions/6755673/c-conversion-operator-for-converting-to-function-pointer) – Scheff's Cat May 04 '20 at 10:29

1 Answers1

3
  • So why I cannot directly define conversion for my class using the type int(*)(int, int) but I can with a type alias?

The grammar for the "operator TYPE" name of a conversion function is much more restricted than a more general declarator or type-id. It doesn't allow parentheses at all, only a type specifier (like a type alias name, unsigned int, a class name, etc.), combinations of the *, &, &&, const and volatile tokens, and [[attributes]]. I can't say exactly why, but complicated declarations like that are tricky to write, read, and parse. Maybe there's a potential ambiguity in some case if more were allowed, or maybe they just didn't want to require compilers to have to figure this one out.

Also, if it were allowed, maybe the form would be operator int (*())(int, int); and not operator (int(*)(int, int))()? Or maybe that doesn't make sense either. See? Tricky.

  • Why I get value 1 in the first statement which is erroneous and get it correct in the second statement using parenthesis?

Function call syntax has higher precedence than C-style cast. So

(int(*)(int, int))Foo()(5, 7)    // (1)
(int(*)(int, int)) (Foo()(5, 7)) // (2), same as (1)
((int(*)(int, int))Foo()) (5, 7) // (3), not the same

Expression (1) or (2) evaluates by first creating a temporary Foo. It's followed by function call syntax, and Foo doesn't define an operator(), but C++ will also check if it implicitly converts to a pointer or reference to function and it does, so the (5, 7) does the implicit conversion and calls the resulting pointer to add, giving 12. This is cast to the function pointer type, which has implementation-defined results. There is no operator<< declared for function pointers, but there is one for bool, and a function pointer can implicitly convert to bool. Presumably the result of the strange cast was not a null pointer value, so the end result is true, and you see the value 1. (If you had done std::cout << std::boolalpha earlier, you should see true or an appropriate translation instead.)

One piece of this, besides the operator precedence misunderstanding, is the dangers of a C-style cast, which can do very many different things, some not usually intended. Use static_cast<int(*)(int,int)>(Foo())(5,7) instead, and everything's fine. Or if we accidentally typed static_cast<int(*)(int,int)>(Foo()(5,7)) instead, the compiler gives an error about converting from int to int(*)(int,int), since only reinterpret_cast or C-style cast may do that.

  • I get the warning from first statement: Description Resource Path Location Type cast to pointer from integer of different size [-Wint-to-pointer-cast] main.cpp /MyCppProj line 31 C/C++ Problem

Even though the C-style cast forces the conversion from int to function pointer to be valid, the compiler is warning that int doesn't have enough bytes to represent a function pointer. It's assuming the int value earlier came from casting a function pointer to some numeric type, and this is meant to convert back, but whenever it was converted from a type large enough to int, the pointer value was lost.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I do really appreciate it! Clear now for me thank you so much. – Maestro May 04 '20 at 13:04
  • I've seen some working solution as: `(*operator int() const)(int, int){return sub;}` what do you think? It seems to me so strange! doesn't it? – Maestro May 04 '20 at 13:16
  • Wow, that's interesting. Looks like g++ accepts it, but clang++ and MSVC reject ([compare](https://godbolt.org/z/C4ydT6)). And I don't think [\[class.conv.fct\]/1](https://timsong-cpp.github.io/cppwp/class.conv.fct#1) allows it. – aschepler May 04 '20 at 23:13