It makes some intuitive sense why const is necessary here, as the number hasn't really gotten a place to land so you can't pass it by reference, but is there a more formal explanation?
#include <iostream>
int number()
{
return 8;
}
int greet(const int& q)
{
std::cout << "HI!";
return q;
}
int main()
{
return greet(number());
}
Normally, you'd pass by value, since integers are cheap to copy and have value semantics.
The expression number() is an rvalue (specifically a prvalue), so you can't bind it with a non-const reference. You can use an rvalue by copying it, and you're allow to bind with a const reference.
Const references are special, in that they extend the lifetime of objects, in a way that non-const references do not. And that is the key here.
Related
This has probably been already asked.
Why is it allowed to assign a reference-to-const to a non-const variable?
Why is this allowed
int mut {0};
const int & r_to_c {mut};
mut = 1;
// now r_to_c changed to 1!
// But it was supposed to be a reference to something constant!
?
Sure, I cannot mutate the value from the reference-to-const itself. I cannot
r_to_c = 2;
but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.
Otherwise what guarantees is const giving me? They seem pretty weak, and it seems that this could easily trick programmers to shoot themselves in their foot.
I know that C++ has a reputation for allowing people to shoot themselves in their foot. I don't have a problem with allowing dangerous things. In this case, my problem is that in this case it seems that it is purposefully deceiving, given that the semantics of const here is not the one would expect it.
Mine is a question about the compiler and the language semantics, not about references in particular (I could have asked the same question using a pointer-to-const that is assigned to the address of a non-const variable. Like int mut{0}; const int * p_to_c{&mut};).
Why is the semantics of a reference-to-const (or pointer-to-const) just "you can't use this particular window to modify the thing you see (but if you have other windows that are non-const, you can modify it)" instead of a more powerful "this can only be a window to something that was declared constant and that the compiler guarantees it stays constant"?
[Note on terminology: I use the expression "reference-to-const" instead of "const reference" because a "const reference", interpreted as T& const - consistently with calling T* const a "const pointer" -, does not exist.]
but isn't the const qualification enforcing too little? I would expect, from a promise of const-ness, that binding to mutable variables was disallowed.
No it is not "too little". You are expecting the wrong thing.
First, whether you bind a const reference does not make the object itself const. That would be strange:
void foo(int& x) {
static const int& y = x;
}
When I call foo:
int x = 42;
foo(x);
I cannot know whether somebody else will keep a const reference to my non-const x.
Otherwise what guarantees is const giving me?
You cannot modify something via a const reference:
void bar(const int& x);
int x = 0;
bar(x);
When I call a function that takes a const& then I know that it will not modify my (non-const) parameter. If const references would not bind to non-const objects then there would be no way to make this last example work, i.e. you could pass non-const objects only to functions that do modify them, but not to functions that do not modify them.
P.S. I can understand your confusion. It is sometimes overlooked that holding a constant reference does not imply that the object cannot be modified. Consider this example:
#include <cstddef>
#include <iostream>
struct foo {
const int& x;
};
int main() {
int y = 0;
foo f{x};
std::cout << f.x; // prints 0
y = 42;
std::cout << f.x; // prints 42
}
Printing the value of the member to the screen yields two different results, even though foo::x is a constant reference! It is a "constant reference" not a "reference to a constant". What const actually means here: You cannot modify y through f.x.
The ability to bind a const-reference to a mutable variable is actually a very valuable feature to have in the language. Consider that we might want to have a mutable variable.
int mut {0};
// ... some time later
mut = 1;
This is perfectly reasonable; it's a variable that is going to change during the execution of the program.
Now let's say we want to print the value of this variable, and would like to write a function to do that.
void print(int param) // or 'int &' to avoid a copy,
// but the point here is that it's non-const
{
std::cout << param;
}
This is fine, but clearly the function is not changing the parameter. We would like that to be enforced so that mistakes like param = 42; are caught by the compiler. To do that, we would make param a const & parameter.
void print(int const & param);
It would be quite unfortunate if we couldn't call this function with arguments that are non-const. After all, we don't care that the parameter might be modified outside the function. We just want to say that the parameter is guaranteed not to be modified by print, and binding a const & to a mutable variable serves exactly that purpose.
A reference to a const object is not the same as a const reference to a non const object, but C++'s type system does not distinguish them.
This is sort of a violation of the LSP; it has the same kind of problem as a reference to a mutable square and rectangle do.
You can create "true const" but you need help at declaration.
template<class T>
struct true_const {
const T value;
};
a true_const<int>& or true_const<T> const& can be passed around as a reference, and nobody can edit it (without invoking UB) "behind your back".
Of course, a function taking a true const cannot also take a normal object.
void bob( true_const<int>& x ) {
auto local = x.value;
call_some_other_function();
assert(local == x.value); // guaranteed to be true
}
void bob( const int& x ) {
auto local = x;
call_some_other_function();
assert(local == x); // NOT guaranteed to be true
}
const fields in classes are truly const; modifying them is undefined behavior.
A thin wrapper around a type that is const within the class is thus a guarantee the data is const. Then take a reference to that.
Now, the true_const could use some operator support.
template<class T>
struct true_const {
const T value;
constexpr T const& get() const { return value; }
constexpr T const& operator*() const { return get(); }
constexpr T const* operator->() const { return std::addressof(value); }
constexpr operator T const&() const { return get(); }
// concepts-defended operator+,==, etc
};
Is it allowed to return a moved value as a const lvalue reference?
include<string>
using namespace std;
class C {
private:
string s;
public:
const string &release() {
return move(s);
}
};
Well, yes but it won't do anything.
The std::move function is just a cast to a rvalue reference. So in effect:
std::string s;
std::move(s); // returns std::string&& to `s`
So it just returns a reference to the object you pass in.
So in your code, you create an rvalue reference to your string s, but you bind that reference to a std::string const&, which cannot be moved from.
You'd be better simply returning the reference directly:
const string &release() {
return s;
}
Or return by move (using exchange):
std::string release() {
return std::exchange(s, std::string{});
// s valid to be reused, thanks to std::exchange
}
The last solution would be to return an rvalue reference, but I wouldn't do that, as it won't guarantee the reference to be moved from.
No, and I don't see why you would want to. std::move is like a cast, to the rvalue-reference type. The point of rvalue references returned from std::move is for their corresponding values to be moved into another object efficiently, possibly changing the original object in the process. A const lvalue reference would not allow you to change the object, so it can't be moved from.
I am calling a function with the signature
void setValue(int& data)
I would like to pass a literal number to it:
setValue(1);
But I get:
error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
Is there a way I can make this work without changing the function (it's in a library) and without assigning each literal value to a variable?
Assuming setValue does not actually modify its argument and just has a wrong signature which you cannot change, here is an approach which is not thread-safe among other things:
#include <iostream>
void setValue(int &i)
{
std::cout << "i = " << i << std::endl;
}
int& evil(int i)
{
static int j;
j = i;
return j;
}
int main()
{
setValue(evil(1));
setValue(evil(2));
}
When you declare the argument as being an int&, you are saying that the function called can change the value and the caller will see the change.
So it is no longer valid to pass a literal value then because how could the function possibly change the given value of a literal?
If you don't want the setValue to be able to change the given value, make the argument either be an int or const int&. And if you do want the setValue function to be able to change the value, then the caller must declare a non-const variable to hold the int and pass in that.
Can I change something at the call site to make it work
The problem with your code is that you declared your function to expect a reference, which means the compiler has to prepare the code to allow the function to change whatever you pass into it at the call site. So yes, sure, you can declare a variable, set it to 1 and call your function with it.
Contrast this with a constant reference in the declaration, where the compiler knows you won't change it inside the function, and then you can pass a literal in without issues. In fact, any logical, thought out design will make setters accept constant parameters because it won't change them, it will just store a possibly processed value in its state.
The answer to „what do I do if a library has a bad interface and I can't change it“ is usually „write a wrapper“. Assuming this is a method of some class BadLibraryClass, you could do something like:
class Wrapper {
public:
BadLibraryClass inner;
setValue(int i) {
inner.setValue(i); // i is an lvalue
}
};
This is just a crude example. Perhaps inner is better off being a pointer, a reference or even a smart pointer. Perhaps you want a conversion operator to BadLibraryClass. Perhaps you can use inheritance to expose other methods of BadLibraryClass.
Two options:
Use the result of assignment:
static int _data;
void myCall() {
setValue((_data = 3));
}
Write a wrapper:
struct setValueW {
int _data;
// constructor
setValueW(int _data) : _data(_data) {
setValue(_data);
}
// if you want to call it again
void operator()() {
setValue(_data);
}
};
void myCall2() {
setValueW(3);
}
AFAIK, references keeps the addresses of the variable. 1 is not variable. It is temporary.
Take a look this article(this is a quote from this site)
c++11 introduced a new kind of reference variable -- an r-value reference
To declare one, use && after a type
int & // type designation for an L-value reference
int && // type designation for an R-value reference
L-value references can only refer to L-values
R-value references can reference to R-values (temporaries)
int x, y, z; // regular variables
int & r = x; // L-value reference to the variable x
int & r2 = x + y; // This would be ILLEGAL, since x + y is an R-value
int && r3 = x + y; // LEGAL. R-value reference, referring to R-value
So you can use (But this is not useful. It may be more useful if you write this in plain without rvalue or lvalue.):
void setValue(int&& data)
setValue(1);
Or you can use that:
void setValue(int& data)
int a = 11;
setValue(a);
Don't forget for second example. If you change the value of data parameter. You will have change the a variable value.
No, you can't.
An lvalue reference like that binds to a variable (roughly speaking).
Your literal is not such a thing. It never had a name, and may not even have a home in memory.
Your two options are the two things you ruled out, I'm afraid.
For what it's worth, this is not your fault: that is a rather poor setter. It should take const int& (which will automatically create a nice temporary variable for you out of the literal!), or even just const int.
Why these definitions are all ok:
int func(int p=255) {
return p;
}
int func1(const int &p=255) {
return p;
}
but this definition:
int func2(int &p=255) {
return p;
}
leads to compile error ?
What is the logic behind it ?
Taking arguments by reference means, you dont work with your local copy of the variable, but with a variable already defined in the scope of the calling function.
While your first example makes sense (you have a local variable p that you can fill with a default value) the second example is a bit more tricky: Usually when using references you expect the variable to have an address, since you want to modify it. For const-refernces, the compiler will still allow you to pass a literal, even if something like "reference to a literal" makes no sense at all.
In the third case the compiler expects you to modify p. But what part of the memory should this modification affect? "255" has no address - therefore it cant be used as a reference.
If you want to have a more detailed explanation, you should probably look for keywords like "rvalue" and "lvalue".
The attempted function definition
auto func2( int& p = 255 )
-> int
{ return p; }
… fails because you can't bind an rvalue to a reference to non-const. Basically that rule is because a simple value like 255 isn't modifiable. While the reference can be used to modify.
One simple solution is to express the default as a separate overload:
auto func2( int& p )
-> int
{ return p; }
auto func2()
-> int
{
int scratchpad = 255;
return func2( scratchpad );
}
A non-const reference must be bound to lvalue (i.e. its address could be got). 255 (i.e. an int literal) is not a lvalue, so int &p=255 fails.
A const reference could be bound to rvalue, and for this case, a temporary int will be created and initialized from 255. The temporary int's lifetime will be the same as the const reference.
int func(int p=255) {
return p;
}
p here is copied by value, and it is defined to exist in the scope of func.
int func2(int &p) {
return p;
}
// e.g. use:
int value = 10;
func2(value); // func2 *could* modify value because it is passed by non-const reference
In this case the compiler here expects p to have a name somewhere in memory (i.e. lvalue), so it can possibly write to it within func2. Passing by non-const reference allows you to modify the variable used in the function call. Since p must belong to someone else somewhere since it can be modified, you can't assign a default value to it.
But what about the const-reference case? Here, the compiler is smart enough to know that p can never be written to since it is const, so it doesn't need to have a name in memory to write to. In cases of a literal being passed (e.g. 255), it (behind the scenes) essentially creates a temporary and passes that temporary variable to the function.
int func1(const int &p=255) {
return p;
}
func1(10);
// Behind the scenes, the compiler creates something along these lines
// since it can never be modified.
const int some_temporary = 10;
func1(some_temporary);
Does casting a variable to another type return a temporary copy of that variable? if so then why can't you reference the temporary variable to a function.
void func(int &i) //error converting parameter 1 from int to int&
{
}
int main()
{
double d = 6.8;
func(int(d));
}
Yes casting returns an rvalue (temporary value), but a mutable reference needs an lvalue.
Try this instead:
int main() {
double d = 6.8;
{
int v = d;
func(v);
d = v; // if the change needs to be reflected back to d.
// note that, even if `func` doesn't change `v`,
// `d` will always be truncated to 6.
}
}
If func is not going to modify i, the input argument should be a const reference, which can accept an rvalue.
void func(const int& i);
(but for primitives func(int i) is going to be more efficient.)
The problem is that when you do int(d) inside the call to func, it creates a temporary object. You can't bind a reference to temporary. At least, not until C++0x comes and we get rvalue references (some compilers support them already, but the implementations may not be totally solid). You need to have an int variable defined to store the converted value, then pass that to the function.