I have this code, which compiles fine:
void foo(int x){std::cout << "value copy" << std::endl;} // foo(5) or foo(n)
void foo(int &x){std::cout << "lvalue ref" << std::endl;} // foo(n) only
void foo(int &&x){std::cout << "rvalue ref" << std::endl;} // foo(5) only
but since i have referenced versions, it's ambiguous.
how are you meant to use this functionality? i thought the compiler would've gave me an error.
i know we can cast functions, but in that case it'd seem pointless.
you'd might as well call them fooL and fooR if you're just gonna cast right?
During overload resolution, when selecting the best viable function, the compiler will select the overload that has the best conversion sequence.
In the case where the parameters of two compared overload are reference binding, the standard defines what is the best binding according to [over.ics.rank]/3.2.3 to [over.ics.rank]/3.2.6. These rule enable the compiler to choose whether foo(int&) or foo(int&&) is a better match for a given argument type.
But there are no rule that specifies if an argument passed by value is better than an argument bound to a reference parameter. So for this simple case where all overloads takes an int either by reference or by value, calling foo will always be ambiguous if the argument is not const. (If the argument type is const qualified, foo(int&) and foo(int&&) are not viable overloads).
Related
The code below:
int i = 1;
const int i_c = 2;
volatile int i_v = 3;
const volatile int i_cv = 4;
typedef std::variant<int, const int, volatile int, const volatile int> TVariant;
TVariant var (i );
TVariant var_c (i_c );
TVariant var_v (i_v );
TVariant var_cv(i_cv);
std::cerr << std::boolalpha;
std::cerr << std::holds_alternative< int>(var ) << std::endl;
std::cerr << std::holds_alternative<const int>(var_c ) << std::endl;
std::cerr << std::holds_alternative< volatile int>(var_v ) << std::endl;
std::cerr << std::holds_alternative<const volatile int>(var_cv) << std::endl;
std::cerr << var .index() << std::endl;
std::cerr << var_c .index() << std::endl;
std::cerr << var_v .index() << std::endl;
std::cerr << var_cv.index() << std::endl;
outputs:
true
false
false
false
0
0
0
0
coliru
And so std::variant converting constructor doesn't take into account const volatile qualifier of the converting-from type. Is it expected behavior?
Information about converting constructor from cppreference.com
Constructs a variant holding the alternative type T_j that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for every T_i from Types...
The problem is that in the case above the overload set of such imaginary function is ambiguous:
void F( int) {}
void F(const int) {}
void F( volatile int) {}
void F(const volatile int) {}
coliru
cppreference.com says nothing about this case. Does the standard specify this?
I'm making my own implementation of std::variant class. My implementation of converting constructor is based on this idea. And the result is the same as shown above (the first suitable alternative is selected, even though there are others). libstdc++ probably implements it in the same way, because it also selects the first suitable alternative. But I'm still wondering if this is correct behavior.
Yeah, this is just how functions work when you pass by value.
The function void foo(int) and the function void foo(const int) and the function void foo(volatile int) and the function void foo(const volatile int) are all the same function.
By extension, there is no distinction for your variant's converting constructor to make, and no meaningful way to use a variant whose alternatives differ only in their top-level cv-qualifier.
(Well, okay, you can emplace with an explicit template argument, as Marek shows, but why? To what end?)
[dcl.fct/5] [..] After producing the list of parameter types, any top-level cv-qualifiers modifying a parameter type are deleted when forming the function type. [..]
Note that you are creating copy of value. This means that const and volatile modifiers can be safely discarded. That is why template always deduces int.
You can force specific type using emplace.
See demo https://coliru.stacked-crooked.com/a/4dd054dc4fa9bb9a
My reading of the standard is that the code should be ill-formed due to ambiguity. It surprises me that both libstdc++ and libc++ appear to allow it.
Here's what [variant.ctor]/12 says:
Let T_j be a type that is determined as follows: build an imaginary function FUN(T_i) for each alternative type T_i. The overload FUN(T_j) selected by overload resolution for the expression FUN(std::forward<T>(t)) defines the alternative T_j which is the type of the contained value after construction.
So four functions are created: initially FUN(int), FUN(const int), FUN(volatile int), and FUN(const volatile int). These are all equivalent signatures, so they could not be overloaded with each other. This paragraph does not really specify what should happen if the overload set cannot actually be built. However, there is a note that strongly implies a particular interpretation:
[ Note:
variant<string, string> v("abc");
is ill-formed, as both alternative types have an equally viable constructor for the argument. —end note]
This note is basically saying that overload resolution cannot distinguish between string and string. In order for that to happen, overload resolution must be done even though the signatures are the same. The two FUN(string)s are not collapsed into a single function.
Note that overload resolution is allowed to consider overloads with identical signatures due to templates. For example:
template <class T> struct Id1 { using type = T; };
template <class T> struct Id2 { using type = T; };
template <class T> void f(typename Id1<T>::type x);
template <class T> void f(typename Id2<T>::type x);
// ...
f<int>(0); // ambiguous
Here, there are two identical signatures of f, and both are submitted to overload resolution but neither is better than the other.
Going back to the Standard's example, it seems that the prescription is to apply the overload resolution procedure even if some of the overloads could not be overloaded with each other as ordinary function declarations. (If you want, imagine that they are all instantiated from templates.) Then, if that overload resolution is ambiguous, the std::variant converting constructor call is ill-formed.
The note does not say that the variant<string, string> example was ill-formed because the type selected by overload resolution occurs twice in the list of alternatives. It says that the overload resolution itself was ambiguous (because the two types had equally viable constructors). This distinction is important. If this example were rejected after the overload resolution stage, an argument could be made that your code is well-formed since the top-level cv-qualifiers would be deleted from the parameter types, making all four overloads FUN(int) so that T_j = int. But since the note suggests a failure during overload resolution, that means your example is ambiguous (as the 4 signatures are equivalent) and this must be diagnosed.
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.
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!
Consider the following example
void foo(const std::function<int()>& f) {
std::cout << f() << std::endl;
}
void foo(const std::function<int(int x)>& f) {
std::cout << f(5) << std::endl;
}
int main() {
foo([](){return 3;});
foo([](int x){return x;});
}
This does not compile, because the call to foo is said to be ambiguous. As far as I understand this is due to the fact, that the lambda function is not a priori a std::function but has to be casted to it and that there is a std::function constructor that takes an arbitrary argument.
Maybe someone can explain to me why anyone would create an implicit constructor that takes an arbitrary argument. However my acutual question is whether there is a workaround, which allows to use the function signature of lambda functions to overload a the function foo. I have tried function pointers, but that didn't work because capturing lambda functions can't be cast to a normal function pointer.
Any help is most welcome.
Your compiler is correct according to C++11. In C++14, a rule is added that says that the constructor template shall not participate in overload resolution unless the type of the argument is actually callable with the std::function's argument types. Therefore, this code is supposed to compile in C++14, but not in C++11. Consider this to be an oversight in C++11.
For now, you can work around this by explicit conversion:
foo(std::function<int()>([](){return 3;}));
http://coliru.stacked-crooked.com/a/26bd4c7e9b88bbd0
An alternative to using std::function is to use templates. Templates avoid the memory allocation overhead associated with std::function. The template type deduction machinery will deduce the correct type of the lambda passed so the call site cast goes away. However you still have is disambiguate the overloads for the no-args vs args case.
You can do this using a trick with trailing return types that behave similar to enable_if.
template<typename Callable>
auto baz(Callable c)
-> decltype(c(5), void())
{
std::cout << c(5) << std::endl;
}
The above overload of baz will only be a valid overload candidate when the template parameter Callable can be called with an argument of 5.
You can put more advanced mechanisms on top of this to make it more general (ie. variadic pack expansion of args into callable) but I wanted to show the basic mechanism working.
What do the characters in bold mean in this sentence extracted from paragraph §5.2.2/1 of the C++11 Standard?
There are two kinds of function call: ordinary function call and member function (9.3) call. A function call is a postfix expression followed by parentheses containing a possibly empty, comma-separated list of expressions which constitute the arguments to the function. For an ordinary function call, the postfix expression shall be either an lvalue that refers to a function (in which case the function-to-pointer standard conversion (4.3) is suppressed on the postfix expression), or it shall have pointer to function type.
Edit
Basically what I'm asking is: when the Standard says "(in which case the function-to-pointer standard conversion is suppressed on the postfix expression)" does it mean that this suppression is for good or that it will be revoked later (e.g. after function overloading)?
Edit1
In my opinion the word "suppressed" above is misleading since it gives the impression that the conversion from function name to a function pointer might never be done by the compiler. I believe that's not the case. This conversion will always occur after the function overloading process is finished, since only at this point, the compiler knows exactly which function to call. That's not the case when the program initializes a function pointer. In this situation, the compiler knows the function to be called, so there will be no overloading, as the example below shows.
#include <iostream>
int foo(int i) { std::cout << "int" << '\n'; return i * i; }
double foo(double d) { std::cout << "double" << '\n'; return d * d; }
int main()
{
int(*pf)(int) = foo; // no overloading here!
std::cout << pf(10.) << '\n'; // calls foo(int)
std::cout << foo(10.) << '\n'; // call foo(double) after function overloading. Therefore, only after function
// overloading is finished, the function name foo() is converted into a function-pointer.
}
The code prints:
int
100
double
100
Absent any statement in the Standard to the contrary, the suppression is presumably permanent. I think the conversion is suppressed because (i) overload resolution needs to take place and (ii) it is not necessary in this situation. Indeed, it would be impossible to convert to a pointer before we knew exactly which function is being called. After overload resolution, we know we are directly calling the function, so a conversion would be pointless.