Can you please explain me the difference between mechanism of the following:
int function();
template<class T>
void function2(T&);
void main() {
function2(function()); // compiler error, instantiated as int &
const int& v = function();
function2(v); // okay, instantiated as const int&
}
is my reasoning correct with respect to instantiation?
why is not first instantiated as const T&?
Thank you
Because function returns a non-const value. Only objects can be const, because they store some state that could be modified if it weren't const. What you return there is not an object, but a pure value. Conceptually, they are not modifiable (like enumeration constants, for example), but they are not const qualified (like, again, enumeration constants).
I think that you might be confused between rvalues and the const qualifier. function returns a non-const rvalue temporary of type int, so the compiler deduces T to be int, as it should. As you point out, you can bind a temporary to a const ref (c++03 12.2/5), but the compiler will not add cv qualifiers to make a function call well formed. Since you can't control the template function, there are two ways around this (in addition to the solution you posted).
(1) Explicit template parameters: function2<const int>(function())
(2) cv qualify return: const int function();
Both of these solutions are well formed. (1) seems the better solution, IMHO, since (2) is unconventional and silly.
Edit: Actually, the deduced type can be more cv-qualified than the argument for a ref template argument, but only if type deduction would otherwise fail (c++03 14.8.2.1/3). In this case, type deduction doesn't fail, but results in a malformed function call (SFINAE does not apply, because the template function specialization itself is not malformed).
If the intent of the template author was to not modify the argument, it should be declared as a const reference argument, so this may be a bug in the template library, or it may modify the argument, in which case what you are doing will fail where the function attempts to modify the argument.
Edit: As FredOverflow points out, non-class rvalues are always cv unqualified by the standard 3.10/9. So (2), which works under gcc 4.3, is actually a compiler bug (gcc <4.5, according to FredOverflow).
In this line
function2(function());
after function2 returns, the argument that passes to it might have its value change, but since function() returns and it's just assigned to a temporary variable, but what would happen to this temporary variable after it goes out of scope is the problem, that's why the compiler complaints.
To compile the first call, it is necessary to define function2 with T&& parameter - this is rvalue, reference to temporary object. In the second call, v is lvalue reference, it is OK. If your compiler doesn't support rvalue references, the first call may be compiled only with T parameter, without reference.
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.
So I have the following function:
void scan(std::istream& is, Handler& h);
I want to call it in different ways, like:
scan(std::cin, Handler());
scan(std::ifstream("myfile"), myhandler);
The compiler complains about std::ifstream("myfile") and Handler() of being rvalues being passed as non-const references, so the complaint is legitimate, but what can I do?
Neither function parameters cannot be const (istream is modified while read and the handler changes its state during callbacks).
If I change the parameter types to rvalue references (&&) then I will not be able to pass std::cin and sometimes I really care about the final state of myhandler thus I cannot apply std::move on them neither.
In principle I could make the parameters as universal references via template or auto&& type deduction and thus overload this function for all possible combinations of lvalue and rvalue references, but I have no intention of overloading this function for other types than I have already specified.
Are there any other options?
Somehow this whole move semantics got in the way in such a trivial example.
To convert an rvalue to an lvalue, you can use this lvalue helper function:
template<class T>
T& lvalue_ref(T&& x) { return x; }
And then the call becomes:
scan(lvalue_ref(std::ifstream("myfile")), lvalue_ref(Handler()));
This is safe as the temporaries (the ifstream and Handler) aren't destructed until the end of the full expression. However, note that these are lvalue references to temporaries and as such you must use caution when deciding to use this method. I'm assuming the scan() doesn't hold references/pointers to the arguments after it returns.
For example, do not use it like this:
int& x = lvalue_ref(5);
std::cout << x; // temporary is destructed, therefore Undefined Behavior
Just make sure the lifetime of the returned reference corresponds with the lifetime of the temporary, and you'll be fine.
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.
Can someone explain when rvalue references are preferred over const lvalue references when working as function parameters?
Background:
I was trying to pass a const pointer into a function. Since I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return), I have two choices: the parameter could either be declared as:
void foo(T const* const&); //const lvalue ref to const ptr
or
void foo(T const* &&); //rvalue ref to const ptr
But this rvalue reference cannot be bound to a local variable (which is of lvalue type. But I did remember Scott Meyers coined the term "universal reference" to refer to rvalue reference. This confuses me more.) So my question is, since the first declaration could deal with both cases, when would the second one using rvalue reference be preferred?
Note: In the first approach, the other forms
void foo(const const T* &);
void foo(const T* const&);
didn't work. I guess the reason is that in the latter two I was not consistent in the place where the const qualifiers are put into (please correct me if I'm wrong).
It is very rarely a good idea to pass a pointer by const &: at best it takes the same overhead, at worst it causes extremely complex pointer reseating logic to surprise readers of your code.
Take pointers by value -- T const* -- and things are more sane.
References to non-pointer values make more sense.
Universal references is a technique using rvalue and lvalue references in a type deduction context. It basically only applies when you have a type T&& being deduced from an expression -- in that context T can be X, X& or X const& (or other cv variants).
If T is X& or X const&, the rvalue reference to the lvalue reference collapses into a lvalue reference. It is an example of the standard committee being clever, and it allows auto&&x= based universal reference variables, and perfect forwarding code to be easy to write.
Remark: I wrote this answer under the assumption that T in your question represents some actual data type -- I've chosen int in my examples below.
Background: I was trying to pass a const pointer into a function. [...] I have to consider the cases in which a local pointer is passed in and in which a temporary is passed in (say from a function call return)
You didn't say what you mean by "const pointer". I will first assume you mean a pointer that is itself constant (i.e. the address to which it points cannot be changed).
According to your description, there are basically two ways you get such a pointer:
// Case 1 (what you call a local pointer -- this should be inside some
// function body):
int *const p = 0;
// Case 2, a function that returns a pointer; this is your rvalue case
// in contexts where f() is called and its return value used as a temporary:
int *f()
{ return 0; }
// Note: The temporary returned by this function isn't, strictly speaking,
// constant. It could be modified as long as it is alive. But from
// your description I take it that you have no intentions of doing so
// and/or regard temporaries as generally constant.
Now you can define a function that accepts these two cases as follows:
void g(int *const &arg)
{ }
You can apply this as g(p); to a constant, local pointer such as p defined earlier, as well as to a temporary g(f());. (You could, thirdly, apply it to a non-const local pointer as well, because going from non-const lvalue to const lvalue is never a problem.)
This function g has a function argument arg which is defined as a constant, lvalue reference to an int-pointer. It can bind to a constant (or indeed non-constant) local pointer (such as p) as well as a temporary, because constant lvalue references, unlike non-constant lvalue references, can do that.
Remark: It's not clear to me why, in this case, you need the function argument to be a reference at all. You could simply declare void g(int *const arg) (no ampersand) and do without a reference. Reasons include a) You cannot modify it anyway; b) In all real-world implementations, the reference will take just as much (or as little) space as the pointer itself, so there is no point in avoiding a copy.
Anyway. If you want you can also define a second version of g specifically for rvalue references:
void g(int *&& arg)
{ }
This can only be applied to the temporary, not to the local pointer, because the function argument is defined as an rvalue reference, which can bind to temporaries, but not to lvalues.
However, if by "const pointer" you actually mean a pointer-to-const, i.e. a pointer that can be changed to different addresses, but does not have the power to modify the value stored at those addresses, the declarations are a bit different. The keyword const must then be put before the asterisk, and for better clarity best before the type specifier int:
// Declare local pointer-to-const:
const int *p = 0;
// Function that returns a pointer-to-const:
const int *f()
{ return 0; }
A function that can accept these two would then be declared as:
void g(const int *const &arg)
{ }
The first const means we are talking about pointers-to-const, and the second const ensures we have a constant lvalue-reference, which can bind to both rvalues and lvalues. Note that this function can not modify what arg points to, because arg is declared as a constant lvalue reference. In the case where arg binds to the temporary, that is probably what we want anyway (as stated above). But in the case where the function is called as g(p);, we might actually want to modify the local pointer p from within g. If you want g to have this power, you need to define two versions of it:
void g(const int *&& arg)
{ /* Can bind to temporaries, but not modify them. */ }
void g(const int *& arg)
{ /* Can bind to local variables and modify what they point at */ }
Remark 1: Your original declaration const int *const &const is useless (and not even accepted by GCC). It would mean a "constant reference to a constant pointer to constant int", but since a reference to a constant pointer is implicitly itself a const-reference, the final const is superfluous (and not provided for by the Standard).
Remark 2: Universal references are not the same as rvalue references. Universal references are declared as T &&arg where T is a template parameter. Depending on what T refers to in each instantiation of the template, this may be an lvalue reference or an rvalue reference -- hence its "universal" character. This has nothing to do with your use case, anyway, though, since you are dealing with pointers T * here (even if we assume that T is a template parameter).
You do not want to differentiate between a temporary pointer and an lvalue pointer. That looks to me like something that's bound to fail rather sooner than later.
Universal Refs only apply in template functions like
template<class T> void foo(T && fwdref);
Note that a "universal ref" is not the same as an rvalue ref.