Re: rvalue-reference-with-default instead of NRV
Am 14.03.2014 17:22, schrieb Florian Weimer:
It is possible to use an rvalue reference with a default argument
instead of a regular named return value, like this:
std::string
readlink(const char *path, std::string &&result = std::string())
{
char buf[PATH_MAX];
ssize_t ret = readlink(path, buf, sizeof(buf));
if (ret < 0) {
throw unistd_exception();
}
result.assign(buf, ret);
return result;
}
This kind of "is it possible?" question is easily solved by testing it. :)
I think it should compile, yes. But running it will involve copying the
string object 'result' refers to into the return value. 'result' is an
rvalue reference but it is itself an lvalue expression. The return
expression only treats function-local objects implicitly as rvalues when
it comtes to constructing the return value (in case elision cannot be
done for whatever reason including an ABI not supporting that) and the
object 'result' refers to does not qualify. So, to avoid the copy you
would have to use an explicit move like this:
string dosomething(int param, string&& result = string())
{
....
return move(result);
}
But I would avoid something like this entirely until a benchmark shows
that this sort of "code uglification" has real benefits that are
actually worth the hassle.
(With C++98, you'd have to use a const reference and cast away
constness.)
As a result, a caller can use the default (and a completely new string
object will be allocated), or it can re-use an existing string object
for the return value.
Is this an idiom other programmers would recognize?
I guess not.
Curiously, it is possible to use this idiom to extend the lifetime of
a temporary to the full expression of the caller. So you can write
this:
const char *
readlink2(const char *path, std::string &&result = std::string())
{
char buf[PATH_MAX];
ssize_t ret = readlink(path, buf, sizeof(buf));
if (ret < 0) {
throw unistd_exception();
}
result.assign(buf, ret);
return result.c_str();
}
And a call like
puts(readlink2(path));
will work the same way as:
puts(readlink1(path).c_str());
The default parameter should live long enough -- that is -- until the
semicolon after puts(). Default parameter objects are constructed in the
context of the caller.
Of course, this is fairly risky because assigning the result const
char * to a local variable does not extend the lifetime of the
temporary object, invalidating the pointer too early. For this
reason, I don't think this a good idea for actual code.
On that we agree.
Cheers!
sg
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]