This is a very minimal example:
class Foo
{
public:
Foo(int x) {};
};
void ProcessFoo(Foo& foo)
{
}
int main()
{
ProcessFoo(Foo(42));
return 0;
}
The above compiles fine on Visual Studio, but generates an error on Linux and Mac.
Compiling the above generates this:
$ g++ -std=c++11 -c newfile.cpp
newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
ProcessFoo(Foo(42));
^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
void ProcessFoo(Foo& foo)
I've found three workarounds:
Create a temp variable for the invocation of ProcessFoo.
Like this:
Foo foo42(42);
ProcessFoo(foo42);
ProcessFoo takes a const reference: void ProcessFoo(const Foo& foo)
ProcessFoo just lets Foo get passed by value. void ProcessFoo(Foo foo)
Why is the compiler forbidding my original code? (What is it guarding against)? What is it about each of the three workarounds above that satisfies the compiler? What would MSVC allow it, but not g++?
By design, C++ only allows a temporary to be passed to a const reference, value, or rvalue reference. The idea is that a function taking a non-const reference parameter is stating that it wants to modify the parameter and allowing it to go back to the caller. Doing so with a temporary is meaningless and most likely an error.
And I don't know what version of g++ you're running. It doesn't work here: http://coliru.stacked-crooked.com/a/43096cb398cbc973
Why is the compiler forbidding my original code?
Because it is forbidden by the Standard:
8.5.3 References 5
...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
[ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
...
'
What is it guarding against?
Inadvertently modifying an object that is going to be destructed after the function call.
What is it about each of the three workarounds above that satisfies the compiler?
1 Creates a named object and 3 a copy.
2 Works because the lifetime of the object is simply extended, and changes to it are prevented at the same time.
What would MSVC allow it, but not g++?
Because it is a language extension. Disable it by going to Property Pages->C/C++->Language->Disable Language Extensions and you'll get an error.
Why is the compiler forbidding my original code?
MSVC has an extension that allows temporaries to bind to non-const lvalue-references. Of course this isn't a standard-conforming feature so I would stay away from it to be portable. For example, it doesn't work with the latest versions of GCC and Clang as you've seen.
What is it about each of the three workarounds above that satisfies the compiler?
Back in C++03, expressions could only be lvalues or rvalues. References could only designate the "lvalueness" of an object, and so it was used with the intention of aliasing a preexisting object. By contrast, rvalues don't exist beyond the expression in which they appear. Also, the end result of references was normally to copy or modify the object, and it doesn't make much sense to the language to modify an rvalue like 55 for example.
The rules allow you to bind an rvalue to a lvalue-reference to const, in which case the temporary's lifetime is extended to the lifetime of the reference. When you take an object by value the object is copied.
With C++11 we have rvalue-references and xvalues which were made for the purpose of exchanging ownership. With this is lessens the usefulness of lvalue-references to const. Moreover, taking by-value causes a move if it is an rvalue.
Once you declared the prototype for ProcessFoo as
void ProcessFoo(Foo& foo)
You are conveying your intent as the formal parameter "foo" is subject to modification as it is not being passed by const &.
At the call-site,
ProcessFoo(Foo(42));
Foo(42) is creating a temporary stack object that is not modifiable. It is okay to pass-by-value or pass-by-ref-to-const to a method.
As you listed yourself, satisfying those constraints, makes the compiler happy.
is giving you an object that is not compiler generated and is under your control.
Informs the compiler that the method guarantees const-ness of the object.
Informs the compiler that this (temporary) object is passed by value and hence no issues.
Related
This is a very minimal example:
class Foo
{
public:
Foo(int x) {};
};
void ProcessFoo(Foo& foo)
{
}
int main()
{
ProcessFoo(Foo(42));
return 0;
}
The above compiles fine on Visual Studio, but generates an error on Linux and Mac.
Compiling the above generates this:
$ g++ -std=c++11 -c newfile.cpp
newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
ProcessFoo(Foo(42));
^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
void ProcessFoo(Foo& foo)
I've found three workarounds:
Create a temp variable for the invocation of ProcessFoo.
Like this:
Foo foo42(42);
ProcessFoo(foo42);
ProcessFoo takes a const reference: void ProcessFoo(const Foo& foo)
ProcessFoo just lets Foo get passed by value. void ProcessFoo(Foo foo)
Why is the compiler forbidding my original code? (What is it guarding against)? What is it about each of the three workarounds above that satisfies the compiler? What would MSVC allow it, but not g++?
By design, C++ only allows a temporary to be passed to a const reference, value, or rvalue reference. The idea is that a function taking a non-const reference parameter is stating that it wants to modify the parameter and allowing it to go back to the caller. Doing so with a temporary is meaningless and most likely an error.
And I don't know what version of g++ you're running. It doesn't work here: http://coliru.stacked-crooked.com/a/43096cb398cbc973
Why is the compiler forbidding my original code?
Because it is forbidden by the Standard:
8.5.3 References 5
...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
[ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
...
'
What is it guarding against?
Inadvertently modifying an object that is going to be destructed after the function call.
What is it about each of the three workarounds above that satisfies the compiler?
1 Creates a named object and 3 a copy.
2 Works because the lifetime of the object is simply extended, and changes to it are prevented at the same time.
What would MSVC allow it, but not g++?
Because it is a language extension. Disable it by going to Property Pages->C/C++->Language->Disable Language Extensions and you'll get an error.
Why is the compiler forbidding my original code?
MSVC has an extension that allows temporaries to bind to non-const lvalue-references. Of course this isn't a standard-conforming feature so I would stay away from it to be portable. For example, it doesn't work with the latest versions of GCC and Clang as you've seen.
What is it about each of the three workarounds above that satisfies the compiler?
Back in C++03, expressions could only be lvalues or rvalues. References could only designate the "lvalueness" of an object, and so it was used with the intention of aliasing a preexisting object. By contrast, rvalues don't exist beyond the expression in which they appear. Also, the end result of references was normally to copy or modify the object, and it doesn't make much sense to the language to modify an rvalue like 55 for example.
The rules allow you to bind an rvalue to a lvalue-reference to const, in which case the temporary's lifetime is extended to the lifetime of the reference. When you take an object by value the object is copied.
With C++11 we have rvalue-references and xvalues which were made for the purpose of exchanging ownership. With this is lessens the usefulness of lvalue-references to const. Moreover, taking by-value causes a move if it is an rvalue.
Once you declared the prototype for ProcessFoo as
void ProcessFoo(Foo& foo)
You are conveying your intent as the formal parameter "foo" is subject to modification as it is not being passed by const &.
At the call-site,
ProcessFoo(Foo(42));
Foo(42) is creating a temporary stack object that is not modifiable. It is okay to pass-by-value or pass-by-ref-to-const to a method.
As you listed yourself, satisfying those constraints, makes the compiler happy.
is giving you an object that is not compiler generated and is under your control.
Informs the compiler that the method guarantees const-ness of the object.
Informs the compiler that this (temporary) object is passed by value and hence no issues.
This is a very minimal example:
class Foo
{
public:
Foo(int x) {};
};
void ProcessFoo(Foo& foo)
{
}
int main()
{
ProcessFoo(Foo(42));
return 0;
}
The above compiles fine on Visual Studio, but generates an error on Linux and Mac.
Compiling the above generates this:
$ g++ -std=c++11 -c newfile.cpp
newfile.cpp: In function ‘int main()’:
newfile.cpp:23:23: error: invalid initialization of non-const reference of type ‘Foo&’ from an rvalue of type ‘Foo’
ProcessFoo(Foo(42));
^
newfile.cpp:14:6: note: in passing argument 1 of ‘void ProcessFoo(Foo&)’
void ProcessFoo(Foo& foo)
I've found three workarounds:
Create a temp variable for the invocation of ProcessFoo.
Like this:
Foo foo42(42);
ProcessFoo(foo42);
ProcessFoo takes a const reference: void ProcessFoo(const Foo& foo)
ProcessFoo just lets Foo get passed by value. void ProcessFoo(Foo foo)
Why is the compiler forbidding my original code? (What is it guarding against)? What is it about each of the three workarounds above that satisfies the compiler? What would MSVC allow it, but not g++?
By design, C++ only allows a temporary to be passed to a const reference, value, or rvalue reference. The idea is that a function taking a non-const reference parameter is stating that it wants to modify the parameter and allowing it to go back to the caller. Doing so with a temporary is meaningless and most likely an error.
And I don't know what version of g++ you're running. It doesn't work here: http://coliru.stacked-crooked.com/a/43096cb398cbc973
Why is the compiler forbidding my original code?
Because it is forbidden by the Standard:
8.5.3 References 5
...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.
[ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
...
'
What is it guarding against?
Inadvertently modifying an object that is going to be destructed after the function call.
What is it about each of the three workarounds above that satisfies the compiler?
1 Creates a named object and 3 a copy.
2 Works because the lifetime of the object is simply extended, and changes to it are prevented at the same time.
What would MSVC allow it, but not g++?
Because it is a language extension. Disable it by going to Property Pages->C/C++->Language->Disable Language Extensions and you'll get an error.
Why is the compiler forbidding my original code?
MSVC has an extension that allows temporaries to bind to non-const lvalue-references. Of course this isn't a standard-conforming feature so I would stay away from it to be portable. For example, it doesn't work with the latest versions of GCC and Clang as you've seen.
What is it about each of the three workarounds above that satisfies the compiler?
Back in C++03, expressions could only be lvalues or rvalues. References could only designate the "lvalueness" of an object, and so it was used with the intention of aliasing a preexisting object. By contrast, rvalues don't exist beyond the expression in which they appear. Also, the end result of references was normally to copy or modify the object, and it doesn't make much sense to the language to modify an rvalue like 55 for example.
The rules allow you to bind an rvalue to a lvalue-reference to const, in which case the temporary's lifetime is extended to the lifetime of the reference. When you take an object by value the object is copied.
With C++11 we have rvalue-references and xvalues which were made for the purpose of exchanging ownership. With this is lessens the usefulness of lvalue-references to const. Moreover, taking by-value causes a move if it is an rvalue.
Once you declared the prototype for ProcessFoo as
void ProcessFoo(Foo& foo)
You are conveying your intent as the formal parameter "foo" is subject to modification as it is not being passed by const &.
At the call-site,
ProcessFoo(Foo(42));
Foo(42) is creating a temporary stack object that is not modifiable. It is okay to pass-by-value or pass-by-ref-to-const to a method.
As you listed yourself, satisfying those constraints, makes the compiler happy.
is giving you an object that is not compiler generated and is under your control.
Informs the compiler that the method guarantees const-ness of the object.
Informs the compiler that this (temporary) object is passed by value and hence no issues.
Once upon a time, I assumed that code like this would fail:
const MyClass& obj = MyClass();
obj.DoSomething();
because the MyClass object would be destroyed at the end of its full-expression, leaving obj as a dangling reference. However, I learned (here) that this isn't true; the standard actually has a special provision that allows const references to keep temporaries alive until said references are destroyed themselves. But, it was emphasized, only const references have this power. Today I ran the code below in VS2012 as an experiment.
struct Foo
{
Foo() { std::cout << "ctor" << std::endl; }
~Foo() { std::cout << "dtor" << std::endl; }
};
void f()
{
Foo& f = Foo();
std::cout << "Hello world" << std::endl;
}
The output when calling f() was:
ctor
Hello world
dtor
So I had a look at the C++11 draft standard, and only found this (§ 12.2/4):
There are two contexts in which temporaries are destroyed at a
different point than the end of the full-expression. The first context [doesn't
apply]. The second context is when a reference is bound to a
temporary. The temporary to which the reference is bound or the
temporary that is the complete object of a subobject to which the
reference is bound persists for the lifetime of the reference.
The word const is conspicuously absent from the above. So; has this behavior been changed for C++11, was I wrong about the const thing to begin with, or does VS2012 have a bug and I just haven't found the relevant part of the standard?
The behavior hasn't changed, you just need to turn your warning level up to /W4. VisualStudio implements the lifetime extension rule even for non-const lvalue references as a compiler extension. In this context, binding an rvalue to the non-const reference behaves the same as if you were binding it to a const reference.
With /W4 you'd see this:
warning C4239: nonstandard extension used : 'initializing' : conversion from 'Foo' to 'Foo &'
1> A non-const reference may only be bound to an lvalue
The text disallowing binding of an rvalue to a non-const lvalue reference can be found in §8.5.3/5
— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const), or the reference shall be an rvalue reference.[ Example:
double& rd2 = 2.0; // error: not an lvalue and reference not const
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const
—end example ]
The second half of the quoted statement is what allows binding of a temporary to an rvalue reference, as shown in litb's answer.
string &&s = string("hello");
This, combined with the lifetime extension rule in §12.2/5, means the lifetime of the temporary will now match the lifetime of the (rvalue) reference it is bound to.
The word const was never present in this section. The rule
has always been (from as long as I can remember) that
a temporary used to initialize a reference has its lifetime
extended to match that of the reference, regardless of the type
of the reference.
Sometime in the late 1980's (very pre-standard), C++ introduced
the rule that a temporary could not be used to initialize
a non-const reference. Initializing a non-const reference with
a temporary would still extend the lifetime (presumably), but
since you couldn't do it... Most compilers implemented
a transition period, in which such an initialization would only
emit a warning (and the lifetime was extended).
For some reason, when Microsoft finally decided to implement
C++ (some time in the early 1990's), they decided not to
implement the new rule, and allowed initialization of
a non-const reference with a temporary (without even a warning,
at a time when most other vendors were gradually turning the
warning into an error). And of course, the implemented the
usual lifetime rule.
Finally, in C++11, new types of references were introduced,
which allowed (or even required) initialization with
a temporary. The rule about the lifetime of temporaries hasn't
changed, though; a temporary which is used to initialize
a reference (regardless of the type of reference) has its
lifetime extended.
(With a few exceptions: I would not recommend using a temporary
to initialize a class member reference in an initialization
list.)
No, because rvalue references don't need to be const, so the Standard quote is correct
string &&s = string("hello");
The lifetime is still enlargened. The constraints that make the code invalid for non-const lvalue reference is at clause 8 (notice that it is not the right place to just add "const" and "rvalue reference" etc in the paragraph you quoted. You need an active rejection of such bindings, not just saying that the lifetime of such bindings are not enlarged because you would leave the binding itself still wellformed).
I'm told that, in C++03, temporaries are implicitly non-modifiable.
However, the following compiles for me on GCC 4.3.4 (in C++03 mode):
cout << static_cast<stringstream&>(stringstream() << 3).str();
How is this compiling?
(I am not talking about the rules regarding temporaries binding to references.)
I'm told that, in C++03, temporaries are implicitly non-modifiable.
That is not correct. Temporaries are created, among other circumstances, by evaluating rvalues, and there are both non-const rvalues and const rvalues. The value category of an expression and the constness of the object it denotes are mostly orthogonal 1. Observe:
std::string foo();
const std::string bar();
Given the above function declarations, the expression foo() is a non-const rvalue whose evaluation creates a non-const temporary, and bar() is a const rvalue that creates a const temporary.
Note that you can call any member function on a non-const rvalue, allowing you to modify the object:
foo().append(" was created by foo") // okay, modifying a non-const temporary
bar().append(" was created by bar") // error, modifying a const temporary
Since operator= is a member function, you can even assign to non-const rvalues:
std::string("hello") = "world";
This should be enough evidence to convince you that temporaries are not implicitly const.
1: An exception are scalar rvalues such as 42. They are always non-const.
First, there's a difference between "modifying a temporary" and "modifying an object through an rvalue". I'll consider the latter, since the former is not really useful to discuss [1].
I found the following at 3.10/10 (3.10/5 in C++11):
An lvalue for an object is necessary
in order to modify the object except
that an rvalue of class type can also
be used to modify its referent under
certain circumstances. [Example: a
member function called for an object
(9.3) can modify the object. ]
So, rvalues are not const per-se but they are non-modifiable under all but some certain circumstances.
However, that a member function call can modify an rvalue would seem to indicate to me that the vast majority of cases for modifying an object through an rvalue are satisfied.
In particular, the assertion (in the original question I linked to) that (obj1+obj2).show() is not valid for non-const show() [ugh, why?!] was false.
So, the answer is (changing the question wording slightly for the conclusion) that rvalues, as accessed through member functions, are not inherently non-modifiable.
[1] - Notably, if you can obtain an lvalue to the temporary from the original rvalue, you can do whatever you like with it:
#include <cstring>
struct standard_layout {
standard_layout();
int i;
};
standard_layout* global;
standard_layout::standard_layout()
{
global = this;
}
void modifying_an_object_through_lvalue(standard_layout&&)
{
// Modifying through an *lvalue* here!
std::memset(global, 0, sizeof(standard_layout));
}
int main()
{
// we pass a temporary, but we only modify it through
// an lvalue, which is fine
modifying_an_object_through_lvalue(standard_layout{});
}
(Thanks to Luc Danton for the code!)
(I asked a variation of this question on comp.std.c++ but didn't get an answer.)
Why does the call to f(arg) in this code call the const ref overload of f?
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
My intuition says that the f(string &&) overload should be chosen, because arg needs to be converted to a temporary no matter what, and the temporary matches the rvalue reference better than the lvalue reference.
This is not what happens in GCC and MSVC (edit: Thanks Sumant: it doesn't happen in GCC 4.3-4.5). In at least G++ and MSVC, any lvalue does not bind to an rvalue reference argument, even if there is an intermediate temporary created. Indeed, if the const ref overload isn't present, the compilers diagnose an error. However, writing f(arg + 0) or f(std::string(arg)) does choose the rvalue reference overload as you would expect.
From my reading of the C++0x standard, it seems like the implicit conversion of a const char * to a string should be considered when considering if f(string &&) is viable, just as when passing a const lvalue ref arguments. Section 13.3 (overload resolution) doesn't differentiate between rvalue refs and const references in too many places. Also, it seems that the rule that prevents lvalues from binding to rvalue references (13.3.3.1.4/3) shouldn't apply if there's an intermediate temporary - after all, it's perfectly safe to move from the temporary.
Is this:
Me misreading/misunderstand the standard, where the implemented behavior is the intended behavior, and there's some good reason why my example should behave the way it does?
A mistake that the compiler vendors have somehow all made? Or a mistake based on common implementation strategies? Or a mistake in e.g. GCC (where this lvalue/rvalue reference binding rule was first implemented), that was copied by other vendors?
A defect in the standard, or an unintended consequence, or something that should be clarified?
EDIT: I have a follow-on question that is related: C++0x rvalue references - lvalues-rvalue binding
GCC is doing it wrong according the FCD. The FCD says at 8.5.3 about reference binding
If the reference is an lvalue reference and the initializer expression is an [lvalue / class type] ...
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type.
Your case for the call to the std::string && matches none of them, because the initializer is an lvalue. It doesn't get to the place to create a temporary rvalue, because that toplevel bullet already requires an rvalue.
Now, overload resolution doesn't directly use reference binding to see whether there exist an implicit conversion sequence. Instead, it says at 13.3.3.1.4/2
When a parameter of reference type is not bound directly to an argument expression, the conversion sequence is the one required to convert the argument expression to the underlying type of the reference according to 13.3.3.1.
Thus, overload resolution figures out a winner, even though that winner may actually not be able to bind to that argument. For example:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&);
void f(B);
int main() { A a; f(a.bits); }
Reference binding at 8.5 forbids bitfields to bind to lvalue references. But overload resolution says that the conversion sequence is the one converting to int, thus succeeding even though when the call is made later, the call is ill-formed. Thus my bitfields example is ill-formed. If it was to choose the B version, it would have succeeded, but needed a user defined conversion.
However, there exist two exceptions for that rule. These are
Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference to non-const to an rvalue or binding an rvalue reference to an lvalue.
Thus, the following call is valid:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }
And thus, your example calls the const T& version
void f(const std::string &);
void f(std::string &&); // would bind to lvalue!
void g(const char * arg) { f(arg); }
However, if you say f(arg + 0), you create an rvalue, and thus the second function is viable.
It was a defect in the standard draft you read. This defect got in as a side effect of some eager editing to disallow binding of rvalue references to lvalues for safety reasons.
Your intuition is right. Of course, there is no harm in allowing an rvalue reference to refer to some unnamed temporary even if the initializer was an lvalue expression. After all, this is what rvalue references are for. The issue you observed has been fixed last year. The upcoming standard will mandate that the second overload will be picked in your example where the rvalue reference will refer to some temporary string object.
The rule fix made it into the draft n3225.pdf (2010-11-27):
[...]
Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference and the initializer expression shall be an rvalue or have a function type. [...]
[...]
Otherwise, a temporary of [...] is created [...]
double&& rrd3 = i; // rrd3 refers to temporary with value 2.0
But N3225 seems to have missed to say what i is in this example. The latest draft N3290 contains these examples:
double d2 = 1.0;
double&& rrd2 = d2; // error: copying lvalue of related type
int i3 = 2;
double&& rrd3 = i3; // rrd3 refers to temporary with value 2.0
Since your MSVC version was released before this issue got fixed, it still handles rvalue references according to the old rules. The next MSVC version is expected to implement the new rvalue reference rules (dubbed "rvalue references 2.1" by MSVC developers) see link.
I did not see the behavior mentioned by Doug on g++. g++ 4.5 and 4.4.3 both call f(string &&) as expected but VS2010 calls f(const string &). Which g++ version are you using?
A lot of things in the current draft of the standard need clarification, if you ask me. And the compilers are still developing, so it's hard to trust their help.
It looks pretty clear that your intuition is right… temporaries of any kind are supposed to bind to rvalue references. For example, §3.10, the new "taxonomy" section, categorically defines temporaries as rvalues.
The problem may be that the RR argument specification is insufficient to invoke the creation of a temporary. §5.2.2/5: "Where a parameter is of const reference type a temporary object is introduced if needed." That sounds suspiciously exclusive.
Seems to slip through the cracks again at §13.3.3.1/6: (emphasis mine)
When the parameter type is not a reference, the implicit conversion sequence models a copy-initialization of the parameter from the argument expression. The implicit conversion sequence is the one required to convert the argument expression to a prvalue of the type of the parameter.
Note that copy-initialization string &&rr = "hello"; works fine in GCC.
EDIT: Actually the problem doesn't exist on my version of GCC. I'm still trying to figure out how the second standard conversion of the user-defined conversion sequence relates to forming an rvalue reference. (Is RR formation a conversion at all? Or is it dictated by scattered tidbits like 5.2.2/5?)
Take a look at this:
http://blogs.msdn.com/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx
rvalue references: overload resolution
It looks like your case is: "Lvalues strongly prefer binding to lvalue references".
I don't know if that has changed in the latest versions of the standard, but it used to say something like "if in doubt, don't use the rvalue reference". Probably for compatibility reasons.
If you want the move semantics, use f(std::move(arg)), that works with both compilers.