I've been playing with the return type deduction supported in g++ with -std=c++1y.
If you prototype a function with an explicit return type, and then later try to define the function with return type deduction, the compiler complains of an ambiguous old declaration:
std::string some_function();
...
auto some_function(){ return std::string{"FOO"}; } //fails to compile
Is there a good reason why this doesn't work?
My rationale for using return type deduction in the definition is to keep the code clean, but want an explicit type in the prototype for self-documenting reasons. Recommendations on best practices for when and when not to use return type deduction would be appreciated :)
To be more clear, I would like answers to:
1. Is this an implementation mistake in the compiler? (I am fairly sure it is not)
2. Could this type of deduction be done, but isn't allowed by the proposal to the standard? If so, why not?
3. If this is really ambiguous, what are some examples where deducing the type and trying to match it with an explicit forward declaration would get you into trouble?
4. Are there deeper implementation specific issues behind this?
5. Is it simply an oversight?
It's because of how the function tables and overloaded functions work in C++.
When the compiler reads your std::string some_function();
it creates a spot for it to reference in the binary and says if "this function is ever called jump to this spot".
So we have a vtable that looks like this...
(Address offset) (Symbol)
0x???????? std::string somefunction();
Now it gets to your auto some_function() {...}
. Normally it would first look in the function table to see if auto somefunction();
exists in the table or some variation there of, but the compiler notices that this is a implementation and it has the auto keyword so to reduce the entropy it writes *blank* some_function();
to the function table and ties to solve the return type.
Now the Function table looks like this...
(Address offset) (Symbol)
0x???????? std::string somefunction();
0x???????? ????? somefunction();
So it chugs along compiling code into binary when it finds out the return type which in this case is std::string
. The compiler now knows what the return type is so it goes to the function table and changes auto somefunction();
too std::string somefunction();
.
Now the Function table looks like this...
(Address offset) (Symbol)
0x???????? std::string somefunction();
0x???????? std::string somefunction();
Now the compiler goes back and continues to compile the function. Once it's done it goes back and finishes up the vtable only to find the same symbol is in there twice. It's now ambiguous to which symbol we are referring too.
So what is the reason for this?
Not 100% sure but vtables are made long before your code is worked down enough to allow for the deduction type to be made. So the only option the compiler has to work with at that stage in time is to just assume it's a new symbol. It's just something I've noticed looking at symbol tables all day and writing my own C++11 compiler.
I however can in no way speak for other compilers where lots of optimizations and other steps are introduced, I just work with the bare bones, however that is my understanding of the standard.
One last thing the type is completely arbitrary. It doesn't even matter if they don't match at the time of deduction. It never get's that far. The problem arises that there are two of the same symbols in the table, and you can't overload on return types.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With