This appears to be an error, but I just want to confirm. Is the following well formed? If not, why not?
#include <iostream>
struct X
{
int value;
constexpr X(int value) : value(value) {}
constexpr X& do_something(int x)
{
return x < 3 ? *this : throw("FAIL");
//return *this;
}
};
int main()
{
X x(2);
std::cout << x.do_something(1).value << std::endl;
}
Under VC++2015 R3
with default solution switches, I get:
warning C4172: returning address of local variable or temporary
g++ (GCC) 5.4.0
with switches -Wall -pedantic
I get:
error: invalid initialization of non-const reference of type ‘X&’ from an rvalue of type ‘X’
return x < 3 ? *this : throw("FAIL");
^
However, clang version 3.9.1 (tags/RELEASE_391/final)
with the switches -Wall -pedantic
doesn't have a problem with it.
Using a return *this;
of course doesn't have a problem.
Since you have a C++14 tag, the code is 100% well-formed C++14.
Core issue 1560 removed the gratuitous lvalue-to-rvalue conversion here, and as a defect report resolution it should be eventually applied all the way back to the C++98/03 mode of compilers offering such a mode.
See also GCC bug 64372.
C++11 15/2 says:
A try-block is a statement (Clause 6). A throw-expression is of type void...
Then 5.16/2: (from a question that @Fred Larson Mjollnir'd and then un-duped)
If either the second or the third operand has type void, then the lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the second and third operands, and one of the following shall hold:
— The second or the third operand (but not both) is a throw-expression (15.1); the result is of the type of the other and is a prvalue.
— Both the second and the third operands have type void; the result is of type void and is a prvalue.
So from here we see that the conditional expression operator (ternary) portion is legal. But the result of the ?:
is a prvalue which then cannot be legally bound to the non-const X&
return type and is in fact ill-formed.
Why not use something that's both sure to work and is easier to read instead? For example:
X& do_something(int x)
{
if (x >= 3)
throw("FAIL");
return *this;
}
I'm not an expert regarding the compilers, but my guess is the example you posted will work if the compiler handles that particular corner case. The way I read this, your do_something(int x)
function expands to this:
X& do_something(int x)
{
if x < 3
return *this;
else
return throw("FAIL");
}
Now, throw
is a keyword and as such doesn't have a return value, so strictly speaking this is a compile-time error. However, I guess compilers (or at least some of them) are kind enough to go: "Oh, OK... There's no return value here, but throw
is a special situation that'll cause the function to not return, anyway, so let's not complain and let the exception handling at runtime take care of this."
I personally don't like to take my chances with compilers and try to keep things as straight as possible, but... while that may arguably be a better practice, it may also be just a personal preference.
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