Inconsistent overload resolution with rvalue references - c++

My understanding of overload resolution is that 'T&&' is generally a better match than 'const T&'. However, I'm seeing some inconsistent behavior among compilers with this trivial example:
#include <iostream>
void Func(const double& a)
{
(void)a;
std::cout << "[lvalue]\n";
}
void Func(double&& a)
{
(void)a;
std::cout << "[rvalue]\n";
}
template <typename T>
void TFunc(T&& a)
{
Func(a);
}
int main ()
{
TFunc(5.5f);
return 0;
}
Clang 3.2 will print [rvalue]. However, the VS2013 32bit/64bit compilers will print [lvalue]. If I change '5.5f' to '5.5', then both compilers will print [lvalue].
I can understand why the VS compiler chooses the 'const double&' version since I don't have a std::forward call to preserve the '&&' of the parameter. However, I still don't understand what makes clang think that the '&&' overload is the better choice.
Why does adding an implicit float to double conversion affect the behavior in clang? Who is right?

When you call with 5.5f, T is float, and the Func call effectively becomes Func(double(a)). The argument is a temporary, and so rvalue overload should be chosen.
When you call with 5.5, T is double, and no temporary is created in a call to Func(a). A named variable cannot bind to an rvalue reference, so lvalue overload is the only choice.
MSVC has a long-standing misfeature that allows temporaries to bind to non-const lvalue references. This is why it incorrectly chooses lvalue overload even in the first case. Try compiling with /Za (disable language extensions) - I believe it would match clang's behavior then.

Related

Is the local variable returned by a function automatically moved in C++20?

Please consider a C++ program, where the function foo builds its returning U object from a local variable of type int using one of two constructors:
struct U {
U(int) {}
U(int&&) {}
};
U foo(int a = 0) { return a; }
int main() { foo(); }
In C++17 mode the program is accepted by all compilers, demo: https://gcc.godbolt.org/z/b8hhEh948
However in C++20 mode, GCC rejects it with the error:
In function 'U foo(int)':
<source>:6:27: error: conversion from 'int' to 'U' is ambiguous
6 | U foo(int a = 0) { return a; }
| ^
<source>:3:5: note: candidate: 'U::U(int&&)'
3 | U(int&&) {}
| ^
<source>:2:5: note: candidate: 'U::U(int)'
2 | U(int) {}
| ^
demo: https://gcc.godbolt.org/z/fMvEPMGhq
I think this is because of C++20 feature P1825R0: Merged wording for P0527R1 and P1155R3 (more implicit moves)
And the function foo according to this feature must be equivalent to
U foo(int a = 0) { return std::move(a); }
which is rejected due to constructor selection ambiguity by all compilers, demo: https://gcc.godbolt.org/z/TjWeP965q
Is GCC the only right compiler in above example in C++20 mode?
What happens here is that the way that gcc went about implementing P1825.
In this example:
U foo(int a) {
return a;
}
The C++17 and C++20 language rules (no change here) are that we first treat a as an rvalue and if that overload resolution fails (in C++17, it was also required to bind to int&&), then we treat a as an lvalue. With that rule, this code works - the overload resolution with a as an xvalue fails due to ambiguity (because U is a silly type), so we fallback to treating a as an lvalue, and that succeeds.
But gcc's implementation doesn't do that. Instead it treats a as an xvalue that can also bind to non-const lvalue references, and performs a single round of overload resolution (it does so in an effort to avoid breaking some code, see here). That single round of overload resolution is ambiguous, because simply treating a as an xvalue is ambiguous and there's no lvalue ref constructor that is relevant here, so gcc rejects the example.
But in this case, I'd say this is U's fault rather than gcc's. If U either (a) had two constructors that took int const& and int&& as is usual or (b) had a single constructor that took an int, the example would compile fine. Note that in the (b) case, the C++17 rule would perform a copy but the C++20 rule would perform a move (since we no longer require that the constructor specifically takes an rvalue reference).

Why doew GCC compiles function overloaded with value, rvalue and lvalue parameter?

I don't understand why this code compiles:
#include <iostream>
class T {
};
void fun(T) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void fun(const T&) { // Why does this compile?
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void fun(const T&&) { // Why does this compile?
}
int main() {
return 0;
}
The overloads T and const T& are always conflicting, so I don't understand why GCC compiles it.
I have readen that like "a parameter of category value can't be overloaded by a parameter of rvalue or lvalue".
If the overload with T and const T&& works, does it mean that it will be impossible to pass a rvalue to this function in any way, because any call would be ambiguous ? Or is it a way to disambiguiate the call ?
GCC Version: gcc version 7.3.0 (Ubuntu 7.3.0-27ubuntu1~18.04)
__cplusplus = 201103
The overloads are indeed conflicting (ambiguous) under ordinary overload resolution, but they are still resolvable by explicit means
T a;
static_cast<void(*)(T)>(fun)(a); // calls `T` version
static_cast<void(*)(const T &)>(fun)(a); // calls `const T &` version
although I don't immediately see any use case for it.
As for const T && overload - it has some narrow applicability if you for some reason want to prohibit calling your const T & function with non-lvalue arguments
void fun(const T &) {}
void fun(const T &&) = delete;
int main()
{
T t;
fun(t); // OK
fun(T()); // Error
}
See, for example, how it is done for std::ref, std::cref.
I'll assume that you have defined the type T somewhere preceding this code snippet. Otherwise, of course, the code would not compile.
It's not quite true that if one overload takes T and one takes const T&, then overload resolution can never select one of them over the other. For example, if the argument has type volatile T, then the overload taking T must be selected over the one taking const T&. This corner case aside, though, these functions are always individually callable by bypassing overload resolution entirely:
static_cast<void(*)(T)>(fun)(x); // calls void fun(T) on x
It's not the compiler's job to prevent you from declaring functions that pose issues for overload resolution. A good compiler might, perhaps, warn you; but then again, I don't think such a warning is necessary, since almost no programmer would write code like this, other than as an experiment.

Why is this operator= call ambiguous?

I was making a thin derived class with a forwarding constructor. (Bear with me, I must use GCC 4.7.2, which lacks inherited constructors).
On the first try, I forgot to add the explicit keyword and got an error. Could someone explain exactly why this particular error occurs? I'm having trouble figuring out the sequence of events.
#include <memory>
template<typename T>
struct shared_ptr : std::shared_ptr<T>
{
template<typename...Args>
/*explicit*/ shared_ptr(Args &&... args)
: std::shared_ptr<T>(std::forward<Args>(args)...)
{}
};
struct A {};
struct ConvertsToPtr
{
shared_ptr<A> ptr = shared_ptr<A>(new A());
operator shared_ptr<A> const &() const { return ptr; }
};
int main()
{
shared_ptr<A> ptr;
ptr = ConvertsToPtr(); // error here
return 0;
}
The error:
test.cpp: In function ‘int main()’:
test.cpp:28:23: error: ambiguous overload for ‘operator=’ in ‘ptr = ConvertsToPtr()’
test.cpp:28:23: note: candidates are:
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(const shared_ptr<A>&)
test.cpp:9:8: note: shared_ptr<A>& shared_ptr<A>::operator=(shared_ptr<A>&&)
This is also the case with g++ 4.8.4 with the following:
g++ -g -pedantic --std=c++11 -o test main.cpp
The VS2015 settings are all defaulted.
The problem is that the compiler tries to convert a temporary returned by ConvertsToPtr() to a shared_ptr object. When the compiler is used with explicit keyword, then this conversion never occurs using the constructor. However, while examining with gdb it appears that instead it is using the shared_ptr<A> const &() conversion function to match the appropriate Type. This conversion then returns a const shared_ptr & which has no ambiguity when invoking the assignment operator (this is also match the findings of wojciech Frohmberg).
However, if the explicit is omitted, then an object of shared_ptr is returned. this can be matched either to rvalue version of the assignment operator or the const lvalue version.
According to N4296, Table-11, then we have, after the construction with the conversion constructor, a rvalue of shared_ptr object. However the overload resolution finds two matches, which both ranks under Exact Match (the rvalue version is Identity matching while the other is under Qualification matching).
I did check also on VS2015 and like stated in the comments, it works. But using some cout debugging one can see that the const lvalue assignment rvalue is prioritized over the rvalue const lvalue refrence version counterpart.
EDIT: I looked a little deeper in the standard and add the modification. the deleted text regarding the results VS2015 was wrong, because I didn't define both assignments. When both of assignments were declared it does prefer the rvalue.
I assume that the VS compiler distinct the Identity from the Qualification matching in ranking. However as I conclude, it is the VS compiler that is buggy. the g++ compilers obeys the given standard. However since GCC 5.0 Does work as Visual studio, The possibility of compiler bug is slim, so I would be happy to see another experts insights.
EDIT: In 13.3.3.2 one of the draw breakers, after the better ranking I wrote about it, is:
— S1 and S2 are reference bindings (8.5.3) and neither refers to an
implicit object parameter of a non-static member function declared
without a ref-qualifier, and S1 binds an rvalue reference to an rvalue
and S2 binds an lvalue reference.
There is an example attached showing that a given rvalue (not rvalue reference) is supposed to match a const int && over const int &. Therefore I guess, it is safe to assume that it is relevant to our case, even if we have && type and not const && type. I guess after all that GCC 4.7,4.8 is buggy after all.

Is function overloading by reference allowed when there is no ambiguity?

Consider following code:
#include <iostream>
void foo(int m);
void foo(int &k);
int main()
{
foo(5); // ok, because there is no ambiguity
int m = 5;
//foo(m); // compile-time error, because of ambiguity
foo(m + 0); // ok, because it's an expression of type int and not object's lvalue
}
void foo(int m)
{
std::cout << "by value\n";
}
void foo(int &k)
{
std::cout << "by reference\n";
}
I understand that it introduces ambiguity for foo(m), but is this allowed, when expression is of type int (or another that can be converted to int)?
I have tried to find some standard reference on this, yet with no luck.
Disclaimer: Note that it's not duplicate of Function Overloading Based on Value vs. Const Reference. The const references are different as they can be assigned with rvalues, as opposite to "ordinary", non-const references.
13.1 [over.load] is pretty clear (apart from a multi-page note) about which functions cannot be overloaded in the same scope.
Your case is not listed there, and you can declare those overloads, you just can't necessarily use them easily. You could call the lvalue one like so:
void (*f)(int&) = foo;
f(m);
This avoids the ambiguity that happens when you call foo(m).
Aside: another way to write foo(m + 0) is simply foo(+m), the unary + operator converts the lvalue to an rvalue, so the foo(int) overload is called.
Yes, it is allowed.
There is no rule to prevent this overload.
[C++14: 13.1/1]: Not all function declarations can be overloaded. Those that cannot be overloaded are specified here. [..]
[C++14: 13.1/2]: (blah blah lots of exceptions not including any for this case)
It would be extremely limiting for the language to prohibit function overloads that may be ambiguous in certain scenarios with certain calls, and for no good reason I might add!

C++ nested referece directly vs via template argument

Just a question something I found interesting when working with stl. In the below code, the last two lines in the main function will cause the error (indicated in the comments). However, the test_func compiles fine. Since type being passed to the template function is a reference type and the function itself applies the & operator aren't these two things essentially the same? well, apparently not cause one of them compiles and the other doesn't. Anyone know why?
class File {
private:
std::string name_;
public:
File(std::string n) : name_(n) {}
std::string name() const { return name_; }
};
std::ostream& operator<<(std::ostream& os, const File& f)
{
os << f.name();
return os;
}
template <class T> void test_func(const T& v)
{
T& v1(v);
std::cout << "File:" << v1 << std::endl;
}
typedef File& FileRef;
int main(int argc, char* argv[])
{
File f("test_file");
test_func<File&>(f);
// FileRef& fRef1(f); ==> error; cannot declare reference to 'class File&'
// File&& fRef2(f); ==> error; expected unqualified-id before '&&' token
}
UPDATE: I came across this when working with bind1st and bind2nd functions in ; they are defined just like test_func in the text book (stroustrup in Chapter 18 section on binders) so it can't be wrong.
The first commented line is legal, and your compiler is probably not conforming with C++11. Because of C++11's reference collapsing rules, in fact, it should declare an lvalue reference to File named fRef1 and bind it to the lvalue f.
The second commented line is illegal: you cannot bind an rvalue reference to an lvalue. However, the error you are getting seems to indicate that the compiler does not understand the && token.
If you are using Clang or GCC, make sure you are compiling with the -std=c++11 or -std=c++0x option.
UPDATE:
In C++03, both lines are illegal, and even this function call should be rejected by the compiler:
test_func<File&>(f); // SHOULD BE AN ERROR! (substitution failure)
Per paragraph 14.8.2/2 of the C++03 Standard:
[...] Type deduction may fail for
the following reasons:
— [...]
— Attempting to create a reference to a reference type or a reference to void.
— [...]
This can mean two things: either your compiler has a bug, or it intentionally decides to ignore an attempt to create a reference to reference in the context of template argument deduction (and only in that context) - meaning that you're dealing with a compiler extension.
In any case, that function call is ill-formed and therefore not portable.
I think the function works, because the compiler is clever enough not to make a reference of a reference. He gets a reference and wants a reference, so it stays one. I think he simply ignores the second & when you have T& with T already a reference.
I can't explain it in detail but in c++ you can use references mostly exactly like non-references.
In FileRef& he can't ignore that. Here you explicitly say: make a reference of a reference, what can't work.
And && is a logical AND.
ps: substitution failure is not an error (SFINAE)