#include<iostream>
using namespace std;
void foo(const long& a)
{
cout << "long version";
}
void foo( float& a)
{
cout << "float version" << endl;
}
int main()
{
int a = 3;
foo(a);
}
The example above will output "long version", however, according to C++ primer:
When we call an overloaded function that differs on whether a reference or pointer parameter refers or points to const, the compiler uses the constness of the argument to decide which function to call:
The program should output the version that does not contain the constness since they have the same arithmetic conversion precedence, but the program always chooses the one with const for some reason.
Could anyone explain to me why does the compiler choose the const version? Really appreciate your help :)
You are calling the function with an argument of type int, but the function is taking a reference to a long or float, which are different types, as parameter. You cannot bind a reference of one type to another type (up to const/volatile qualifiers and if they are not related through a class hierarchy).
What the compiler therefore tries to do is to create a temporary object of the parameter type initialized from the int argument by conversion (arithmetic conversion) and binds the reference to that.
Temporaries are rvalues, meaning that they cannot be bound to non-const lvalue references. Therefore, after creating a float object from the int passed to it, foos parameter would not be able to bind to it. Consequently the float overload is not viable.
The only overload left is the long one. This one is viable, because after creating the temporary long object, it can bind to a const lvalue reference.
And so the long version is called.
The quote you showed applies (for the most part) only if the argument and the parameters' types match (up to const) and the argument itself is an lvalue. I assume you missed this relevant context surrounding the quoted section.
You cannot call a function taking a non-const lvalue reference with a different type as argument (except if the types are related through a class hierarchy), nor can you call it with a rvalue as argument.
What you seem to be hoping for, is that the compiler will automagically do:
int a = 3;
foo(float(a));
The problem here, is that float(a) is effectively a temporary object, and that object happens to be const. Since foo takes a float&, the compiler cannot match the constant temp, to a non-const (aka mutable) argument. However, the compiler can do this:
int a = 3;
foo(long(a));
Since the argument for foo is const when using long. Given the compiler cannot use the float version, it will always pick the const long version instead.
Related
I was looking at the source code of the std::move function. What it does is to remove the reference and add a right reference(&&).
/**
* #brief Convert a value to an rvalue.
* #param __t A thing of arbitrary type.
* #return The parameter cast to an rvalue-reference to allow moving it.
*/
template<typename _Tp>
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }
So, I was wondering if I can make a right reference and pass it to the function? So, here it is:
void f1(int&&) { std::cout << 1; }
void f1(int&) { std::cout << 2; }
int main() {
int&& x = 1;
f1(static_cast<decltype(x)>(x));
f1(static_cast<int&&>(x));
f1(x);
}
And, output: 112
I figured out that x is type of int&& because first two f1 do the same thing. But, what about the third one? Isn't it an int&& ? Why do I have to static_cast it in order to use it like a right reference?
I know there is a clam in "c++ templates complete guide" which is:
The fact that move semantics is not automatically passed through is intentional
and important. If it weren’t, we would lose the value of a movable object the first time we use it in a function.
I am still curious why they select different function at compile time even they have the same type?
You're confusing types and value categories, they're different things.
As a named variable, x's value category is lvalue, (its type is int&&.) lvalues could be bound to lvalue-reference, but can't be bound to rvalue-reference. So given f1(x);, f1(int&) will be selected.
To get f1(int&&) to be called you need to convert it to rvalue. The 1st and 2nd one select f1(int&&) because explicit conversion make rvalue expressions (more precisely xvalue expressions); which could be bound to rvalue-reference. And std::move works in similar way too.
The following expressions are xvalue expressions:
a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
...
a cast expression to rvalue reference to object type, such as static_cast<char&&>(x);
...
Let's consider the following code snippet
void Test()
{
int x = 0;
int& rx = x;
int* px = &x;
auto apx = px; // deduced type is int*
auto arx = rx; // deduced type is int
}
One could draw an analogy from pointer types expecting that the deduced type of arx is int&, but it is int in fact.
What is the rule in Standard which governs that? What is the reason behind it?
Sometimes I get caught by it in a case like this:
const BigClass& GetBigClass();
...
auto ref_bigclass = GetBigClass(); // unexpected copy is performed
Use auto&:
auto& ref_bigclass = GetBigClass();
References are supposed to be transparent: any operation on them happens on the object they refer to, there is no way to 'get' reference itself.
UPD: This is covered in 7.1.6.4/6:
Once the type of a declarator-id has been determined according to 8.3, the type of the declared variable using the declarator-id is determined from the type of its initializer using the rules for template argument deduction.
And template argument deduction is defined in 14.8.2.1/3:
If template parameter type P is a reference type, the type referred to by P is used for type deduction.
P.S. Note that this is different for decltype: decltype(rx) will yield int& type (7.1.6.2/4).
The simplest way to think about it is comparing it to template argument deduction.
Given:
template<typename T>
void deduce(T) { }
If you call:
deduce(px);
then the template argument T will be deduced as int* and if you call
deduce(rx);
then T will be deduced as int, not int&
You get the same types deduced when using auto.
One could draw an analogy from pointer types expecting that the deduced type of arx is int&
You'd have to have a fairly confused model of the C++ language to make that analogy. Just because they are declared in syntactically similar ways, as Type# with a type and a modifier doesn't make them work the same way. A pointer is a value, an object, and it can be copied and have its value altered by assignment. A reference is not an object, it's a reference to some object. A reference can't be copied (copying it copies the referent) or altered (assigning to it alters the referent). A function that returns a pointer returns an object by value (the object in question being a pointer object), but a function that returns a reference (like your GetBigClass()) returns an object by reference. They're completely different semantics, trying to draw analogies between pointers and references is doomed to failure.
As the title says, I am trying to overload a getter to return both a pointer (mutator method) and a const reference (inspector method) to a member variable.
Please note that the example in the link above uses a reference and a const reference, I don't want this.
#include <vector>
class A {
public:
A() : v() {}
const std::vector<int>& get_v() const {return v;}
std::vector<int>* get_v() {return &v;}
private:
std::vector<int> v;
};
int main() {
A a;
a.get_v()->size(); // ok
a.get_v().size(); // error: request for member ‘size’ in ‘a.A::get_v()’,
// which is of pointer type ‘std::vector<int>*’
// (maybe you meant to use ‘->’ ?)
}
It seems to work ok until I try to use it throwing the error description given above.
Is there a reason (and a fix) for this misbehaviour?
It seems to work ok until I try to use it throwing the error description given above.
a.get_v().size();
That code is obviously wrong, because std::vector<int>* get_v() returns a pointer. The error message is quite clear about trying to access a member of a pointer, that's something you cannot do. To fix it, you need to use the arrow operator to dereference the pointer, like you do on the line above.
How can you say which one is used?
One overload is non-const and can only be called on non-const objects. The other is const and could be called on both const and non-const objects, but it won't ever be called on non-const objects, because the non-const overload is preferred. It's preferred because it doesn't require conversion of the non-const object parameter to const. The call to const member function would require such conversion. Overloads that don't require a conversion of an argument are preferred over those that do require a conversion. Therefore, the non-const version is always called for non-const object arguments.
the following also fails
std::vector<int>& v = a.get_v();
a is still non-const and therefore the overload that returns a pointer is chosen. You cannot bind a non-pointer reference to a pointer.
If you want to call the function that returns a const reference on a non-const object, then you must give it another name so that it doesn't have an overload that would be preferred over it.
Do you have a reference for this?
I'll quote cppreference.com since this is pretty basic stuff - even though the rules are a bit complex. Look up the standard if you doubt it's correctness.
First, little bit about member function overloads:
If any candidate function is a member function (static or non-static), but not a constructor, it is treated as if it has an extra parameter (implicit object parameter) which represents the object for which they are called and appears before the first of the actual parameters.
I think it's obvious that both overloads are candidates, since they have the same name. They're also viable because they have the correct number of arguments and they're convertible. Then, which overload is preferred?
For each pair of viable function F1 and F2, the implicit conversion sequences from the i-th parameter to i-th argument are ranked to determine which one is better
F1 is determined to be a better function than F2 if implicit conversions for all arguments of F1 are not worse than the implicit conversions for all arguments of F2, and
1) there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
...
Ok, so better conversion sequences are preferred... Which is better? Let's first figure out what the conversion sequences are. The only argument is the implicit object argument. The type of the passed argument is non-const A.
The overload that is a non-const has an implicit object argument of type non-const A (it's not really relevant here, but it's passed as a pointer in practice). Non-const A requires no conversion to non-const A because it's an exact match. This will be considered as a identity conversion.
The overload that is const has an implicit object argument of type const A. A can be implicitly converted to const A.
Both conversions are of the same rank. There is lengthy list of rules to go through in that case. None of those apply, until the last part:
f) Or, if not that, S1 and S2 only differ in qualification conversion, and the cv-qualification of the result of S1 is a subset of the cv-qualification of the result of S2
Identity conversion is a subset of a constness conversion. Therefore it's better. I think that identity might also better because cv-conversion requires two conversions when counting the identity conversion... I can't find a rule about that though.
A a isn't const, so compiler can't apply const std::vector<int>& get_v() const {return v;}
This works:
#include <vector>
class A {
public:
A() : v() {}
const std::vector<int>& get_v() const {return v;}
std::vector<int>* get_v() {return &v;}
private:
std::vector<int> v;
};
int main() {
A a;
a.get_v()->size(); // ok
const A b;
b.get_v().size(); // ok
}
Edited - added explanation
Why b.get_v()->size(); fails?
From there:
Best viable function
For each pair of viable function F1 and F2, the implicit conversion sequences from the i-th parameter to i-th argument are ranked to determine which one is better [...]
1) there is at least one argument of F1 whose implicit conversion is better than the corresponding implicit conversion for that argument of F2
Case 1: const std::vector<int>& get_v() const {return v;}
We don't need to convert anything
Case 2: std::vector<int>* get_v() {return &v;}
We need to convert pointer this from const A to A - we can't do it implicitly, only with const_cast
So compiler chose case 1.
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.
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.