Why is this rvalue call ambiguous? - c++

Why is this rvalue call ambiguous? I can have AA and AA& and the compiler will know to use AA&. But when i add in the third option i get an error. Obviously AA&& is a better overload then the others like int for an int is better then long. Why is this ambiguous? Is there a way i can keep all 3 overloads and make it clear which one i want? (Typecasting (AA&&) will not do it).
struct AA{
void*this_;
AA() { this_=this; }
//not valid, use AA&, AA(AA a){ this_=this; }
AA(AA&a){ this_=this; }
AA(AA&&a){ this_=a.this_; }
};
void movetest(AA s) {}
void movetest(AA& s) {}
//This gets me the ambiguous error void movetest(AA&& s) {}
AA&& movetest() { return AA(); }
void MyTestCode2(){
AA a;
AA b(a);
AA c = movetest();
movetest(AA());
}

I can have AA and AA& and the compiler
will know to use AA&
Yes, in the case of movetest(AA());, only movetest(AA) is viable, because a (lvalue) reference to non-const cannot be bound to an rvalue. However, an rvalue reference is said to bind directly to a temporary. Thus, for overload resolution purposes the functions
void movetest(AA)
void movetest(AA&&)
are equal, because the implicit conversion sequences used to convert AA() to AA and AA&&, respectively, are equal. The former is not better, because the direct reference binding is also considered an identity conversion.

Agreed with decltype. This is really no different than this C++03/98 ambiguity:
struct AA {};
void movetest(AA s) {}
void movetest(AA& s) {}
int main()
{
AA a;
movetest(a);
}
test.cpp:9:5: error: call to 'movetest' is ambiguous
movetest(a);
^~~~~~~~
test.cpp:3:6: note: candidate function
void movetest(AA s) {}
^
test.cpp:4:6: note: candidate function
void movetest(AA& s) {}
^
1 error generated.

Related

Why does this assignment operation results in ambiguous function call?

Consider following program it compiles and runs fine:
#include <iostream>
#include <string>
using std::string;
struct BB
{
// generic cast
template<typename T>
operator T() const
{
std::cout<<"Generic cast\n";
return 0;
}
// string cast
operator string() const
{
std::cout<<"string cast\n";
return string("hello");
}
};
int main()
{
BB b;
string s = b; // copy constructor
}
But If I slightly change the main() function's code in like following:
int main()
{
BB b;
string s;
s = b;
}
Compiler give following error message (See live demo here)
[Error] ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string<char>}' and 'BB')
Why this call is ambiguous? What is the reason behind that? It looks like there are so many overloaded operator= like one for char, one for char*, one for const char* etc. That's the above program puts compiler into ambiguity.
Your problem is your template conversion operator.
template<typename T>
operator T() const
{
std::cout << "Generic cast\n";
return 0;
}
Allows BB to be converted to anything. Because of that all of the overloads of std::string::operator= that take a different type can be considered. Since they are all valid there is no way to resolve the ambiguity and you get the error.
If you removed the template conversion then it will compile. The template conversion could also be marked as explicit and then you can use a static_cast to the type you want.
You call operator =, but it would be the same if it were a regular function:
void f(int x);
void f(const string &y);
int main() {
BB b;
f(b);
return 0;
}
Since BB can be cast in either int or string, the compiler has no idea which f function to call.
The only reason why your first example works is because the copy constructor is called there, and it only takes const string& as an argument, so there's no multiple choices.

Overloading on l-values an

How can one invoke the second (overloaded) function?
(This example is present in Savitch's C++ textbook.)
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
I thought the first one is invoked in (a) and the second one in (b). But it is not.
(a) f() = 123; // the first one is invoked.
(b) f() + 3; // the first one is also invoked.
Only member functions can be const. So let's assume f actually is a member function in the textbook.
When the compiler has a choice between a const and a non const member function it will only use the const one if it has to. This is when the object the function is called on is const.
class A {
public:
int &f();
const int& f() const;
};
void func()
{
A a;
a.f(); // calls non const version
const A ca;
ca.f(); // call const version
}
(1) int& f(); // will be used in any l-value invocation
(2) const int& f() const; // will be used in any r-value invocation
These comments are wrong. If they appear in a textbook then I would recommend getting a different textbook.
Version 2 will be used when the expression denoting the object has const type, otherwise version 1 is used. It is nothing at all to do with lvalues and rvalues. Using Eelke's class definition:
A().f(); // invokes (1) on an rvalue
A const a;
a.f(); // invokes (2) on an lvalue
In your examples you don't actually show whether you are working on a const instance or not, but judging by your results, both must have been on a non-const one.
Overloading works on function parameters not by return type.
double f();
int f();
These are not overloading.
But these are:
double f();
int f(int);

Constructor overloading chooses cast operator instead of struct type

I've encountered a weird situation where the compiler chooses to cast a structure even though there's a perfectly good constructor that receives the structure type.
A small example:
struct A
{
operator int() {return 1;}
};
struct B
{
B(A& a) { OutputDebugStringA("A constructor\n"); }
B(int i) { OutputDebugStringA("int constructor\n"); }
};
A test () { A a; return a;};
int _tmain(int argc, _TCHAR* argv[])
{
B b(test());
return 0;
}
Explanation: A has a cast operator to int. B has 2 overloaded constructors, one that accepts A reference, and one that accepts int.
Function test() returns an A object.
For some reason, the compiler decides to cast the return value to an int, and use the constructor that accepts an int. int constructor
Can anyone explain why this happens ? I have a few theories, but I would like an answer that is based on something real (maybe a quote from the standard).
Note:
I can get the expected result (constructor that accepts the type) by changing the constructor signature to: B(const A& a) or B(A&& a)
Your constructor takes a non-const reference to A, which cannot bind to a temporary. You pass it a temporary here:
B b(test());
// ^^^^^^ temporary A
so the only valid constructor is the one taking an int. You can change this behaviour by making the relevant constructor take a const reference:
B(const A& a) { OutputDebugStringA("A constructor\n"); }
//^^^^^
and similarly for B(A&& a); in C++11.
Alternatively, keeping the original constructor signature and passing an lvalue also results in the constructor call:
A a;
B b(a);

Behaviour of copy constructor accepting const reference

In the following Code, in this line
A(A& b)
When using this compiler gives error as
c110.cpp:41: error: no matching function for call to ‘A::A(A)’
c110.cpp:8: note: candidates are: A::A(A&)
But as soon as i convert it into
A(const A& b)
Many many thanx in Advance
No error comes. Why is it so?
Code
class A
{
public:
static int cnt;
A(A& b)
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
A()
{
cnt++;
cout<<"cnt="<<cnt<<endl;
}
~A()
{
cnt--;
cout<<"cnt="<<cnt<<endl;
}
};
int A :: cnt=0;
A fun(A b)
{
return b;
}
int main()
{
A a;
A b=fun(a);
return 0;
}
Non-const references cannot bind to temporaries. If you pass a temporary as parameter, A& is illegal but const A& isn't.
The line
A b=fun(a);
does copy-initialization on the object returned by fun(a), which is a temporary.
Also, the copy constructor shouldn't take a non-const reference because, logically, you don't need to modify the object you're copying from.
I think its always safe to use A(const A&) type of syntax for copy construction rather than A(A&), because RVO may take place or not, its compiler dependent.
As in the above question RVO is not taking place and the temporary is been created, hence A(const A&) is safe to use.

prevent pass-by-ref of temporary object

I have a class that 'remembers' a reference to some object (e.g. an integer variable). I can't have it reference a value that's destructed immediately, and I'm looking for a way to protect the users of my class from doing so by accident.
Is an rvalue-reference overload a good way to prevent a temporary to be passed in?
struct HasRef {
int& a;
HasRef(int& a):a(a){}
void foo(){ a=1; }
};
int main(){
int x=5;
HasRef r1(x);
r1.foo(); // works like intended.
HasRef r2(x+4);
r2.foo(); // dereferences the temporary created by x+4
}
Would a private rvalue overload do?
struct HasRef {
int& a;
HasRef( int& a ):a(a){}
void foo(){ a=1; }
private:
HasRef( int&& a );
};
... HasRef r2(x+1); // doesn't compile => problem solved?
Are there any pitfalls I didn't see?
If you have to store a const reference to some instance of type B into your class A, then surely you want to be ensured, that lifetime of A instance will be exceeded by the lifetime of B instance:
B b{};
A a1{b}; // allowed
A a2{B{}}; // should be denied
B const f() { return B{}; } // const result type may make sense for user-defined types
A a3{f()}; // should also be denied!
To make it possible you should explicitly to = delete; all the constructor overloadings, which can accept rvalues (both const && and &&). For this to achieve you should just to = delete; only const && version of constructor.
struct B {};
struct A
{
B const & b;
A(B const & bb) : b(bb) { ; } // accepts only `B const &` and `B &`
A(B const &&) = delete; // prohibits both `B &&` and `B const &&`
};
This approach allows you to prohibit passing to the constructor all kinds of rvalues.
This also works for built-in scalars. For example, double const f() { return 0.01; }, though it cause a warning like:
warning: 'const' type qualifier on return type has no effect [-Wignored-qualifiers]
it still can has effect if you just = delete; only && version of constructor:
struct A
{
double const & eps;
A(double const & e) : eps(e) {} // binds to `double const &`, `double &` AND ! `double const &&`
A(double &&) = delete; // prohibit to binding only to `double &&`, but not to `double const &&`
};
double const get_eps() { return 0.01; }
A a{0.01}; // hard error
A a{get_eps()}; // no hard error, but it is wrong!
For non-conversion constructors (i.e. non-unary) there is an issue: you may have to provide = delete;-d versions for all the combinatorically possible versions of constructors as follows:
struct A
{
A(B const &, C const &) {}
A(B const &&, C const &&) = delete;
// and also!
A(B const &, C const &&) = delete;
A(B const &&, C const &) = delete;
};
to prohibit mixed-cases like:
B b{};
A a{b, C{}};
Ignoring the fact the code isn't valid and just answering the question about the private overload...
In C++11 I would prefer a deleted function to a private function. It's a bit more explicit that you really can't call it (not even if you're a member or friend of the class.)
N.B. if the deleted constructor is HasRef(int&&)=delete it will not be chosen here:
int i;
HasRef hr(std::forward<const int>(i));
With an argument of type const int&& the HasRef(const int&) constructor would be used, not the HasRef(int&&) one. In this case it would be OK, because i really is an lvalue, but in general that might not be the case, so this might be one of the very rare times when a const rvalue reference is useful:
HasRef(const int&&) = delete;
That shouldn't compile. A good C++ compiler (or really almost any C++ compiler that I've ever seen) will stop that from happening.
I'm guessing you're compiling in MSVS. In that case, turn off language extensions and you should get an error.
Otherwise, not that even marking the reference const extends the lifetime of the temporary until the constructor finishes. After that, you'll refer to an invalid object.