Why a const variable cannot be passed by reference? - c++

My lecture notes said
The argument to a reference parameter must be a variable, not a
constant or an expression.
And thus
int f(double & var); // function prototype
...
const double t = 4.0;
int ret = f(t);
f(t) is illegal.
But I do not understand, why would t be illegal. t is a constant, but still a variable, and I don't think there's anything wrong passing t by reference.

Let me enhance my comment to an answer:
First, t is not a constant, but a const variable. A constant would be 4.0. Your lecture notes are basically saying that you cannot do something like int ret = f(4.0);
Second, what you are seeing is a type mismatch. const as a qualifier is part of the type. You cannot do the following:
const int x = 1;
int& ref_x = x;
error: binding reference of type ‘int&’ to ‘const int’ discards
qualifiers
Nevertheless, it is legal to pass const qualified variables as reference, either use a const reference or cast away the const:
Use a const reference const int& const_int_ref = x;
Use const_cast: int& rx = const_cast<int&>(x);
I prefer the first one whereever possible.

What if the function f modifies var? That shouldn't happen if t is const.
Here's an example implementation of f:
int f(double & var)
{
var += 1;
return var;
}
This will change whatever is passed as an argument. But if the argument was const... tough luck. Then it's not allowed and the compiler explicitly tells you this.
This is the error generated by the compiler:
error: binding reference of type 'double&' to 'const double' discards qualifiers
So by passing a const variable into the function (without a non-const argument), you're telling the compiler to neglecting the constness of the variable in the first place.
If you wish to pass it by reference, pass it by const-reference:
int f(const double & var) // or int f(double const& var)
{
var += 1;
return var;
}
This tells the compiler to retain the const-ness of its arguments.

When you have a reference parameter the object passed needs to actually (at least be able to) occupy memory, a constant (as opposed to a const variable) does not.
I.E. the following would be okay:
void foo(int & n) {
n = 3;
}
void bar() {
int i;
foo(i);
std::cout << "i is " << i << std::endl;
}
but if you had:
void qux() {
foo(3);
}
there would be no object for the assignment in foo to assign to.
Note that you can pass a constant as a reference-to-const (i.e. MyType const &), that's allowed because the assignment issue does not exist when the reference is to a const.

Related

error: invalid initialization of non-const reference of type 'std::function<void()>&' from an rvalue of type 'main()::<lambda()>'|

EDIT: Sorry, I asked this question without a thro understanding of references...
I seem to be getting this error when I run this code...
error: invalid initialization of non-const reference of type 'std::function&' from an rvalue of type 'main()::'
#include <bits/stdc++.h>
using namespace std ;
void printfunction(bool a, function <void()> &b)
{
if (a == true)
{
b() ;
}
}
int main()
{
int value = 45 ;
printfunction(true, [value](){cout << "The value is : " << value ;}) ;
}
But, the error disappears when I add a const before function... like this :
void printfunction(bool a,const function <void()> &b)
The thing is I would like to change the function in the function reference if needed...
Is there any other way to do this? Please let me know if it does indeed exist.
Bye,
Samuel
In printfunction call, lambda expression [value]() {...} argument must be converted to a temporary function<void()> object first.
A reference to non-const function<void()>& only binds to l-values, not temporaries (r-values).
A reference to const, on the other hand, can be bound to temporaries. Which is what you observe.
If you want to modify the std::function, then you'll need to pass a modifiable (lvalue) parameter:
int main()
{
int value = 45;
std::function f = [value](){ std::cout << "The value is : " << value ;};
printfunction(true, f);
}
What you were trying to do isn't much different from writing a function that takes a mutable reference to int (e.g. void foo(int& x)) and then complaining that you can't call foo(5). (The small difference is that the the lambda-expression is converted to a temporary std::function - but that still can't be bound to a non-const reference).
An alternative would be to change printfunction to take its argument by value rather than by reference, so that it has its own copy which it may modify. You'll have to consider the needs of the caller to decide whether that's more appropriate.

invalid initialization of non-const reference of type 'int&', what the reason?

I have the given code, which gets an error:
error: invalid initialization of non-const reference of type 'int&'
from an rvalue of type 'int' const int b = f(a++);
^
int f(int& a)
{
return a;
}
int main() {
// your code goes here
int a = 5;
int b = f(a++);
std::cout << b << std::endl;
return 0;
}
What the cause of this error ?
You can't bind a temporary to a non-const reference.
Post-increment (a++) increments a and returns a temporary with a's old value.
Why are you passing by non-const reference? - it doesn't look like you're changing the parameter inside the function, just just pass by value or const reference.
If you were changing the parameter, what would you expect the behavior to be, considering a++ already changes it? Would the change be intuitive? Legal?
The postfix increment operator on an int returns a temporary value. A temporary value cannot bind to a non-const lvalue reference, because modifying that temporary doesn't make sense. You are trying to bind the temporary to an int&, which is giving an error.
To fix this, either use the pre-increment operator (++a), or take your argument by value (it's better to pass builtin types as value rather than const T&):
int f(int a)
{
return a;
}
This function:
int f(int& a)
accepts non-const reference. Such references must always point to a valid objects, residing at certain memory locations (*).
Post incrementation works as follows:
- save current value as `r`
- increment original variable
- return `r`
That's because result of post-incrementation is a temporary, yielding value from before incrementation. Such temporary must be passed either as value or const reference:
int f(int a) //This will work
int f(const int& a) //And this
(*) In fact, older compilers allowed for such constrcuts. For example, this code will compile under VC6:
struct T {};
void f(T& t)
{
}
int main()
{
f(T());
}
However, such behaviour is not standard-compliant.

Why does C++ allow but ignore the application of const to function types?

I get a real kick out of exploring the unusual corners of C++. Having learned about the real types of functions rather than function pointers from this question, I tried messing around with function typing and came up with this bizarre case:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
const Func*& f = &Foo;
return 0;
}
Since &Foo is an rvalue of type Func*, I figured that I should be able to put it in a const reference, but I get this error from g++ 4.6:
funcTypes.cpp: In function ‘int main()’:
funcTypes.cpp:7:23: error: invalid initialization of non-const reference of type ‘int (*&)(int)’ from an rvalue of type ‘int (*)(int)’
But f is const! It has become apparent to me that the application of const to a function (or reference/reference to pointer etc.) is simply ignored; this code compiles just fine:
template <typename A, typename B>
struct SameType;
template <typename A>
struct SameType<A, A> { };
typedef int Func(int);
int main()
{
SameType<const Func, Func>();
return 0;
}
I'm guessing this is how boost pulls off their is_function type trait, but my question is - why does C++ allow this by ignoring it instead of forbidding it?
EDIT: I realise now that in the first example f is non-const and that const FuncPtr& f = &Foo does work. However, that was just background, the real question is the one above.
But f is const!
No, it's not. You're confusing
const Func*& f = &Foo;
with
Func* const& f = &Foo;
The former is a non-const ref to a const pointer. The latter is a const ref to a non-const pointer.
That's why I always write the const-ness before the */& rather than before the type. I would always write the first case as
Func const*& f = &Foo;
and then read right to left: reference to a pointer to a const Func.
In c++03 it was not ignored, but illformed (and was an sfinae case). I guess they changed that in c++11 because then you can simply have function parameters be const F& and can pass to it rvalue function objects aswell as normal functions.
See this DR which made the change http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#295
&Foo is a pointer. In general, I would suggest avoiding references to pointers (const or no). At least, not unless you know what you're doing.
So you should have:
const Func *f = &Foo;
Or really, you can ditch the const entirely:
Func *f = &Foo;
why does C++ allow this by ignoring it instead of forbidding it?
Because you're talking about two different things.
In C++, there is a difference between a function type and a function pointer. Foo is a function type, specifically int(int). &Foo is a function pointer, of type int(*)(int). A function type degrades into a function pointer, where necessary (much like array types degrade into pointers). But they are distinct (just like arrays).
So your two cases are not the same. Your first case is dealing with a function pointer, and your second case is dealing with a function type (which is what the template argument is deduced as).
As for why function types swallow the const, that's because the values of function types are already implicitly constant. You can't change them. The only operation you can perform on a function type is () (or conversion to function pointer). So a const T is equivalent to T if T is a function type. Visual Studio 2010 actually gives a warning about that.
The following compiles fine:
typedef int Func(int);
int Foo(int x) { return 1; }
int main()
{
Func* const& f = &Foo; //ok
return 0;
}
Be aware that const statements are evaluated along with pointers and references from right to left. The last const to the very left you wrote translates to last possible position right of a Name (C++ FAQ on const placement). Hence
const Func*& f
Is translated by the compiler to
Func const*& f
Hence you get a reference to a constant pointer which is not what you want. Besides I would not use references to function pointer if you do not really have to.
No, f is not const. First of all, it is a reference to some mutable type (that mutable type happens to be a const pointer, i.e. a mutable pointer to something that you promise not to change through that particular pointer). However, with &Foo you are creating a temporary (of type Func*) and you cannot assign a temporary to a mutable reference. You can only assign it to a const reference. And that is precisely what the error message is telling you.
Sometimes the error messages can be a bit cryptic.
I put together an example on ideone to illustrate the types printed by gcc for a variety of things:
#include <iostream>
typedef int(Func)(int);
typedef Func* FuncPtr;
typedef FuncPtr& FuncPtrRef;
typedef FuncPtr const& FuncPtrConstRef;
template <typename T> void DisplayType() { T::foo(); }
int main() {
DisplayType<Func>();
DisplayType<FuncPtr>();
DisplayType<FuncPtrRef>();
DisplayType<FuncPtrConstRef>();
}
The compilation errors give:
Func is of type int ()(int) (not a valid type, should now be fixed in gcc)
FuncPtr is of type int (*)(int)
FuncPtrRef is of type int (*&)(int)
FuncPtrConstRef is of type int (* const&)(int)
In your error message you have int (*&)(int), which is a reference to a non-const pointer to a function type.
You are not allowed to bind an rvalue to a non-const reference.
Note: this has thus nothing to do with const being swallowed, as smparkes correctly diagnosed

c++ implicit conversion rules

I am trying to understand the rules of c++ automatic and explicit conversions in regular or member function calls. I wrote the following code and it fails compilation:
#include <iostream>
#include <string>
using namespace std;
class testExplicit {
public:
int intval;
short shortval;
double doubleval;
char charval;
string strval;
testExplicit(int a1, short a2, double a3, char a4, string& a5):
intval(a1),shortval(a2),doubleval(a3),charval(a4),strval(a5){}
void getVal(int& a) { a = intval; cout << "IntVal\n"; }
// void getVal(short& a) { a = shortval; cout << "Short Val\n"; }
// void getVal(double& a) { a = doubleval; cout << "Double Val\n"; }
// void getVal(char& a) { a = charval; cout << "Char Val\n"; }
// void getVal(string& a) { a = strval; cout << "String Val\n"; }
};
int main( int argc, char **argv ) {
string s ("test Str");
testExplicit test (100,10,10.05,5,s);
int i;
char c;
double d;
short f;
test.getVal(i);
test.getVal(c);
test.getVal(d);
test.getVal(f);
return 0;
}
However, can I conclude that the functions only expect the exact matching parameter? I remember reading that automatic conversions happen according to the conversion rules. Can some shed some light on the correct rules pls?
Here is the error:
test.cpp: In function 'int main(int, char**)':
test.cpp:38: error: no matching function for call to 'testExplicit::getVal(char&)'
test.cpp:17: note: candidates are: void testExplicit::getVal(int&)
test.cpp:39: error: no matching function for call to 'testExplicit::getVal(double&)'
test.cpp:17: note: candidates are: void testExplicit::getVal(int&)
test.cpp:40: error: no matching function for call to 'testExplicit::getVal(short int&)'
test.cpp:17: note: candidates are: void testExplicit::getVal(int&)
Thanks
void getVal(int& a);
This function takes an int by reference. You must pass it an int. This is similar to how if you had a function
void getVal(int* a);
you would need to pass it a pointer to an int (not a pointer to a short or any other type).
One reason for this is that you are able to modify the int from within the function. In order to allow you to pass an object of another type (e.g. a short) to this function, a temporary object of type short would have to be created at runtime and a reference to that temporary object would have to be passed.
This wouldn't be ideal because you might accidentally end up passing the wrong type of object (e.g. a short) and expecting it to be modified by the function, when in fact a temporary copy of type int would be modified by the function, not the original short.
You are, however, permitted to bind const references to temporary objects, so if your function was declared as
void getVal(const int& a);
you would be able to pass it any type that is convertible to int. This makes some sense, since the function cannot modify the referenced object (because it is a const reference), so the "oops, I'm accidentally modifying a temporary object" problem doesn't exist.
Conversions can also take place when you pass by value, but this too makes sense: when you pass by value, a copy has to be made anyway (the copy that is passed to the function by value), so the conversion can take place as part of that copy.
The problem is that if an implicit conversion is required, then a temporary variable is created. You cannot have a non-const reference to a temporary variable, so you would need to make your member function void getVal(const int &a) or void getVal(int a).
Implicit conversion should be avoided. C++ is a strongly typed language it is far better to create functions for the types you wish to accept, use templates, or explicitly cast your type to another type, etc.
Constructor casting or using static_cast<>, dynamic_cast<>, or reinterpret_cast<> are some of the options depending on the situation when you need to convert types from one to another.
Templates can allow you to handle multiple types a little easier.
template<typename T>
void getVal(const T& a)
{
// do something
}
Implicit conversions are done by creating an rvalue (temporary), and rvalues cannot be bound to non-const references. That is the reason why it is not working. If, on the other hand you change the signature to take the arguments either by value or by constant reference, then it will work:
void f( int a ) { std::cout << a << std::endl; }
void g( const int& b ) { std::cout << b << std::endl; }
int main() {
char c = 'a'; // note that this the ASCII value of 'a'
f( c );
g( c );
short s = 4;
f( s );
g( s );
}
Pass by reference means, creating an alias for the same type. So try changing void getVal(int& a) to void getVal(int a), to work for the other function calls differing the passing parameter type

Why is a type qualifier on a return type meaningless?

Say I have this example:
char const * const
foo( ){
/* which is initialized to const char * const */
return str;
}
What is the right way to do it to avoid the compiler warning "type qualifier on return type is meaningless"?
The way you wrote it, it was saying "the returned pointer value is const". But non-class type rvalues are not modifiable (inherited from C), and thus the Standard says non-class type rvalues are never const-qualified (right-most const was ignored even tho specified by you) since the const would be kinda redundant. One doesn't write it - example:
int f();
int main() { f() = 0; } // error anyway!
// const redundant. returned expression still has type "int", even though the
// function-type of g remains "int const()" (potential confusion!)
int const g();
Notice that for the type of "g", the const is significant, but for rvalue expressions generated from type int const the const is ignored. So the following is an error:
int const f();
int f() { } // different return type but same parameters
There is no way known to me you could observe the "const" other than getting at the type of "g" itself (and passing &f to a template and deduce its type, for example). Finally notice that "char const" and "const char" signify the same type. I recommend you to settle with one notion and using that throughout the code.
In C, because function return values, and qualifying values is meaningless.
It may be different in C++, check other answers.
const int i = (const int)42; /* meaningless, the 42 is never gonna change */
int const foo(void); /* meaningless, the value returned from foo is never gonna change */
Only objects can be meaningfully qualified.
const int *ip = (const int *)&errno; /* ok, `ip` points to an object qualified with `const` */
const char *foo(void); /* ok, `foo()` returns a pointer to a qualified object */
None of the previous answers actually answer the "right way to do it" part of the question.
I believe that the answer to this is:
char const * foo( ){
which says you are returning a pointer a constant character.