Ambiguous call when overloaded methods take reverse iterators in arguments - c++

I'm trying to write an overloaded method that returns non-const result only when both the object on which it is called is non-const and iterator passed in the argument is non-const.
(Think of it like the standard methods begin() and begin() const that additionally take an iterator argument.)
I made a version for normal iterators with no problems.
However, for some reason, when I'm trying to do the same for reverse iterators, I get a compilation error about an ambiguous function call.
Here's a minimal example:
#include <vector>
class Foo
{
public:
void bar(std::vector<int>::iterator x) {}
void bar(std::vector<int>::const_iterator x) const {}
void baz(std::vector<int>::reverse_iterator x) {}
void baz(std::vector<int>::const_reverse_iterator x) const {}
};
int main()
{
std::vector<int> v;
Foo foo;
foo.bar(v.cbegin()); // OK
foo.baz(v.crbegin()); // ambiguous
}
For some reason it compiles if I remove const from the second method baz.
It also works in C++20 but currently I cannot use that version.
live demo
How can I make the function baz work in analogous way to the function bar?

Oh the joys of overloading resolution rules and SFINAE.
The methods are equivalent to free functions:
void bbaz(Foo&,std::vector<int>::reverse_iterator){}
void bbaz(const Foo&,std::vector<int>::const_reverse_iterator){}
and your usage becomes:
int main()
{
std::vector<int> v;
Foo foo;
bbaz(foo,v.crbegin());
}
The arguments do not exactly match either call:
foo is Foo&, not const Foo&
v.crbegin() return vector::const_reverse_iterator which is just a different instantiation of the same std::reverse_iterator template as vector::reverse_iterator.
reverse_iterator->std::reverse_iterator<vector::iterator>
const_reverse_iterator-> std::reverse_iterator<vector::const_iterator>
Cause of ambiguity
Now, the issue is that std::reverse_iterator's ctor is not SFINAE-friendly until C++20:
template< class U >
std::reverse_iterator( const std::reverse_iterator<U>& other );
I.e. there is a viable candidate converting std::reverse_iterator<T> to std::reverse_iterator<U> between any T-U pairs. In this case for T=vector::const_iterator, U=vector::iterator. But of course the template instantiation fails later because it cannot convert const int* to int*.
Since that happens in the template function's body, not the signature, it is too late for SFINAE and overloading considers it a viable candidate function, hence the ambiguity since both calls require one implicit conversion - although only the second one would compile.
This is explained in these answers, making this one essentially a duplicate of that question but it would be IMHO cruel to mark it as such without an explanation which I cannot fit into a comment.
C++20 fixes this omission and SFINAEs that ctor - cppreference:
This overload participates in overload resolution only if U is not the same type as Iter and std::convertible_to<const U&, Iter> is modeled (since C++20)
Solution
As pointed in the comments by #Eljay, forcing const Foo& at the call site is one option, one can use C++17 std::as_const:
#include <utility>
std::as_const(foo).baz(v.crbegin());
Fixing this at definition is more tricky, you could use SFINAE to actually force these overloads but that might be a hassle. #fabian 's solution with adding a third overload without const method qualifier seems easiest to me:
void Foo::baz(std::vector<int>::const_reverse_iterator x) {
return std::as_const(*this).baz(x);
}
It works because now it is a better (exact) match for non-const Foos than the still considered vector::reverse_iterator which would not compile anyway.

Related

Generating the necessary ref-qualified overloads for a member function

I have this class:
template<typename T, size_t N>
class Array {
private:
T array[N];
public:
template <typename... InitValues>
constexpr Array(InitValues... init_values)
: array{ init_values... } {}
[[nodiscard]]
consteval int len() const noexcept { return sizeof(array) / sizeof(T); }
}
I would like to know, for such a simple member function, when I should provide the necessary ref-qualified overloads.
With the actual code, I can compile and run the following code:
constexpr collections::Array a = collections::Array<long, 5>{1L, 2L, 3L};
SECTION("length of the array") {
REQUIRE( a.len() == 5 );
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
}
1- Why I can compile the second REQUIRE that contains the call with the rvalue?
Now I am gonna change the len() member function to this:
[[nodiscard]]
consteval int len() const& noexcept { return sizeof(array) / sizeof(T); }
2- Why I can compile both with the const&? I suppose that they are two are different ref-qualified usages. I assume that I can make the call with the first one, which is an lvalue, but can't understand why I can compile the second having defined the len() method as const&.
Last change:
[[nodiscard]]
consteval int len() const&& noexcept { return sizeof(array) / sizeof(T); }
And finally, I got a compiler error on a.get<I>().
'this' argument to member function 'len' is an lvalue, but function has rvalue ref-qualifier
REQUIRE( a.len() == 5 );
that works perfect if I comment that line of code and I just run:
REQUIRE( collections::Array<int, 1>{1}.len() == 1 );
and also I could use std::move(a) to perform the cast of a to an rvalue reference and make the code compile. But I don't want to do that.
What is the correct way of code those examples in terms of ref-qualified overloads?
Don't forget about the questions on the examples above
EDIT:
I will add another member function that could potentially do different things based on the ref-qualified implementation (or that what I am suppose that could happen):
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T get() const noexcept {
return array[I];
}
template <size_t I>
requires concepts::AccessInBounds<I, N>
constexpr T& get() const& noexcept {
return array[I];
}
To question 1: why not? The rule is the same as for lvalues: you can call const member functions regardless of the constness of the object.
To question 2: Because it is meant to be identical to having a const& function parameter: the function can be called with any lvalue or rvalue. It exists primarily to allow you to distinguish between lvalue and rvalue overloads:
class Array {
// These two declarations would be ambiguous for Array rvalues
// int len() const;
// int len() &&;
// These are not: your test expressions will use different overloads
int len() const&;
int len() &&;
};
The two functions in your edit are also ambiguous, for both lvalues and rvalues. A motivating example would be more along these lines: suppose my class provides functionality to some resource that could be expensive to copy, but is cheaper to move, say a std::vector.
template<class T>
class VectorView {
std::vector<T> vector;
public:
// ...
constexpr std::vector<T> const& base() const noexcept { return vector; }
};
Now there is no way for a user of this class to transfer ownership of the vector data back from a view object, even if that would be useful when calling the base() function on an rvalue. Because it is in the spirit of C++ to avoid paying for things you do not need, you could allow this by adding an rvalue-qualified overload that instead returns an rvalue reference using std::move.
So the answer to whether you need this kind of overload is it depends, which is unfortunately also in the spirit of C++. If you were implementing something like my example class for the standard library, then you certainly would, because it is based on std::ranges::owning_view. As you can see on that page, it covers all four possible base()s. If you were instead only using a reference to a source range, it would be unexpected and inappropriate to move from that object, so the related ref_view only has a const base() function like the one I wrote.
Edit As for move semantics, the difference between something like an array and a vector is that Array<T,N> is based on T[N], while std::vector<T> is based on T*. Moving the array requires N move operations (linear time complexity), and whether a move is an improvement over a copy depends on T. Also, it needs memory space for 2N elements. On the other hand, a vector only ever needs three pointers to do its job, so it can be moved in constant time, while copying still takes linear time.
This potential gain is the rationale for move semantics and rvalue references in a nutshell. The ability to also have &&-qualified member functions completes this language feature, but is not as significant as move constructors and assignment functions. I also found the answers to this question useful, as they give some more examples of ref-qualified overloads.

Multiple functions with the same name but their parameters are either constant or received by value or by reference

The title is a bit lengthy, but it's best explained by an example:
Suppose we have the following functions in C++:
void SomeFunction(int num) { //1
}
void SomeFunction(int& num) { //2
}
void SomeFunction(const int& num) { //3
}
void SomeFunction(const int num) { //4
}
All of these are called the same way:
SomeFunction(5);
or
int x = 5;
SomeFunction(x);
When I tried to compile the code, it rightfully says more than one instance of overloaded function "SomeFunction" matches the argument
My question is: Is there a way to tell the compiler which function I meant to call?
I asked my lecturer if it was possible, and she tried something along
SomeFunction< /*some text which I don't remember*/ >(x);
But it didn't work and she asked me to find out and tell her.
I also encounter this post:
How to define two functions with the same name and parameters, if one of them has a reference?
And it seems that 1 and 2 can't be written together, but what about 3 and 4? Can either one of those be called specifically?
1 and 4 have the same signature, so you'll need to drop one of those.
The other functions cannot be called directly, but you could add a template function that allows you to specify the desired parameter type:
template<class Arg>
void Call(void f(Arg), Arg arg)
{
f(arg);
}
// Driver Program to test above functions
int main()
{
int i;
Call<int>(SomeFunction, 1);
Call<int&>(SomeFunction, i);
Call<const int&>(SomeFunction, 1);
}
Alternatively you could use a function pointer to choose the signature.
int i;
static_cast<void(*)(int)>(&SomeFunction)(1);
static_cast<void(*)(int&)>(&SomeFunction)(i);
static_cast<void(*)(const int&)>(&SomeFunction)(1);
It would be preferrable to avoid this scenario though and only define overloads for either references or the signature void SomeFunction(int).
Note:
SomeFunction<some text which I don't remember>(x);
only works for template functions and SomeFunction is not a template function, so this is not an option here.
You can HACK it, and I mean it - it's not a good solution to your problem, by static casting your function explicitly:
static_cast<void(*)(int)>(SomeFunction)(i);
static_cast<void(*)(int&)>(SomeFunction)(i);
static_cast<void(*)(const int&)>(SomeFunction)(i);
Demo
It will work for first 3 overloads. 4th one is equivalent to 1st: quote from the standard [over.load]:
Parameter declarations that differ only in the presence or absence of const and/or volatile are
equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when
determining which function is being declared, defined, or called
and there is an example:
int f (int);
int f (const int); // redeclaration of f(int)
Also note that you cannot call 2nd overload with rvalue (temporary).
The only way I see this working the way your lecturer tried is if SomeFunction is a template and these four overloads are specializations.
template<typename T>
void SomeFunction(T num);
template<>
void SomeFunction<int>(int num) {}
template<>
void SomeFunction<int&>(int& num) {}
template<>
void SomeFunction<const int&>(const int& num) {}
template<>
void SomeFunction<const int>(const int num) {}
Then you can call it as follows.
SomeFunction<int>(x);
SomeFunction<int&>(x);
SomeFunction<const int&>(x);
SomeFunction<const int>(x);
Demo
However, this is incredibly stupid in this context. There are a lot of things wrong with the original overloads.
In the 4th one, the const is completely useless from the caller's perspective, because you can call it the same way you can call the 1st, and the argument is a copy anyway. The const only makes it so the argument is constant inside the function. Moreover, the 1st and 4th overloads cannot both be defined at the same time: the const is actually ignored in the prototype and it leads to a redefinition.
The 3rd overload is also useless because a const int& argument provides no benefit over a int argument. In fact, the compiler probably optimizes that away. The only difference is in the scenario I describe at the end. Of course, if the argument type is more complex (not just int or some other fundamental type), it often makes sense.
The 2nd overload is the only one that can modify the variable you pass as argument. However, if the 1st (or 4th, since it's really the same) overload is present as well, you cannot call the 2nd directly because the call would be ambiguous. You could still call the 1st with an rvalue (basically a literal or an expression like std::move(x)).
If the 2nd and 3rd overloads are the only ones present, then there is no ambiguity and you can call the 2nd with non-const lvalues and the 3rd with const lvalues or rvalues.
Demo

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.

std::function as template parameter

I currently have a map<int, std::wstring>, but for flexibility, I want to be able to assign a lambda expression, returning std::wstring as value in the map.
So I created this template class:
template <typename T>
class ValueOrFunction
{
private:
std::function<T()> m_func;
public:
ValueOrFunction() : m_func(std::function<T()>()) {}
ValueOrFunction(std::function<T()> func) : m_func(func) {}
T operator()() { return m_func(); }
ValueOrFunction& operator= (const T& other)
{
m_func = [other]() -> T { return other; };
return *this;
}
};
and use it like:
typedef ValueOrFunction<std::wstring> ConfigurationValue;
std::map<int, ConfigurationValue> mymap;
mymap[123] = ConfigurationValue([]() -> std::wstring { return L"test"; });
mymap[124] = L"blablabla";
std::wcout << mymap[123]().c_str() << std::endl; // outputs "test"
std::wcout << mymap[124]().c_str() << std::endl; // outputs "blablabla"
Now, I don't want to use the constructor for wrapping the lambda, so I decided to add a second assignment operator, this time for the std::function:
ValueOrFunction& operator= (const std::function<T()>& other)
{
m_func = other;
return *this;
}
This is the point where the compiler starts complaining. The line mymap[124] = L"blablabla"; suddenly results in this error:
error C2593: 'operator = is ambiguous'
IntelliSense gives some more info:
more than one operator "=" matches these operands: function
"ValueOrFunction::operator=(const std::function &other) [with
T=std::wstring]" function "ValueOrFunction::operator=(const T
&other) [with T=std::wstring]" operand types are: ConfigurationValue =
const wchar_t
[10] c:\projects\beta\CppTest\CppTest\CppTest.cpp 37 13 CppTest
So, my question is, why isn't the compiler able to distinguish between std::function<T()> and T? And how can I fix this?
The basic problem is that std::function has a greedy implicit constructor that will attempt to convert anything, and only fail to compile in the body. So if you want to overload with it, either no conversion to the alternative can be allowed, of you need to disable stuff that can convert to the alternative from calling the std::function overload.
The easiest technique would be tag dispatching. Make an operator= that is greedy and set up for perfect forwarding, then manually dispatch to an assign method with a tag:
template<typename U>
void operator=(U&&u){
assign(std::forward<U>(u), std::is_convertible<U, std::wstring>());
}
void assign(std::wstring, std::true_type /*assign_to_string*/);
void assign(std::function<blah>, std::false_type /*assign_to_non_string*/);
basically we are doing manual overload resolution.
More advanced techniques: (probably not needed)
Another approach would be to limit the std::function = with SFINAE on the argument being invoked is valid, but that is messier.
If you have multiple different types competing with your std::function you have to sadly manually dispatch all of them. The way to fix that is to test if your type U is callable with nothing and the result convertible to T, then tag dispatch on that. Stick the non-std::function overloads in the alternative branch, and let usual more traditional overloading to occur for everything else.
There is a subtle difference in that a type convertible to both std::wstring and callable returning something convertible to T ends up being dispatched to different overloads than the original simple solution above, because the tests used are not actually mutually exclusive. For full manual emulation of C++ overloading (corrected for std::functions stupidity) you need to make that case ambiguous!
The last advanced thing to do would be to use auto and trailing return types to improve the ability of other code to detect if your = is valid. Personally, I would not do this before C++14 except under duress, unless I was writing some serious library code.
Both std::function and std::wstring have conversion operators that could take the literal wide string you are passing. In both cases the conversions are user defined and thus the conversion sequence takes the same precedence, causing the ambiguity. This is the root cause of the error.

Universal references and std::initializer_list

In his "C++ and Beyond 2012: Universal References" presentation, Scott repeatedly stresses the point, that universal references handle/bind to everything and thus overloading a function that already takes a universal reference parameter does not make sense.
I had no reason to doubt that until I mingled them with std::initializer_list.
Here is a short example:
#include <iostream>
#include <initializer_list>
using namespace std;
template <typename T>
void foo(T&&) { cout << "universal reference" << endl; }
template <typename T>
void foo(initializer_list<T>) { cout << "initializer list" << endl; }
template <typename T>
void goo(T&&) { cout << "universal reference" << endl; }
template <typename T>
void goo(initializer_list<T> const&) { cout << "initializer list" << endl; }
int main(){
auto il = {4,5,6};
foo( {1,2,3} );
foo( il );
goo( {1,2,3} );
goo( il );
return 0;
}
Oddly enough, VC11 Nov 2012 CTP complains about ambiguity (error C2668: 'foo' : ambiguous call to overloaded function). Yet even more suprising is, that gcc-4.7.2, gcc-4.9.0 and clang-3.4 agree on the following output:
initializer list
initializer list
initializer list
universal reference
So apparently it is possible (with gcc and clang) to overload functions taking universal references with initializer_lists but when using the auto + { expr } => initializer_list-idiom it does even matter whether one takes the initializer_list by value or by const&.
At least to me that behavior was totally surprising.
Which behavior conforms to the standard? Does anyone know the logic behind that?
Here's the crux: Deducing a type from a braced-init-list ({expr...}) doesn't work for template arguments, only auto. With template arguments, you get a deduction failure, and the overload is removed from consideration. This leads to the first and third output.
it does even matter whether one takes the initializer_list by value or by const&
foo: For any X, two overloads taking X and X& parameters are ambiguous for an lvalue argument - both are equally viable (same for X vs X&& for rvalues).
struct X{};
void f(X);
void f(X&);
X x;
f(x); // error: ambiguous overloads
However, partial ordering rules step in here (§14.5.6.2), and the function taking a generic std::initializer_list is more specialized than the generic one taking anything.
goo: For two overloads with X& and X const& parameters and a X& argument, the first one is more viable because the second overload requires a Qualification conversion from X& to X const& (§13.3.3.1.2/1 Table 12 and §13.3.3.2/3 third sub-bullet).
If Scott really says that he's wrong, and it's another problem with the misleading "universal references" mental model he's teaching.
So-called "universal references" are greedy, and might match when you don't want or expect them to, but that doesn't mean they are always the best match.
Non-template overloads can be an exact match and will be preferred to the "universal reference", e.g. this selects the non-template
bool f(int) { return true; }
template<typename T> void f(T&&) { }
bool b = f(0);
And template overloads can be more specialized than the "universal reference" and so will be chosen by overload resolution. e.g.
template<typename T> struct A { };
template<typename T> void f(T&&) { }
template<typename T> bool f(A<T>) { return true; }
bool b = f(A<int>());
DR 1164 confirms that even f(T&) is more specialized than f(T&&) and will be preferred for lvalues.
In two of your cases the initializer_list overloads are not only more specialized, but a braced-init-list such as {1,2,3} can never be deduced by template argument deduction.
The explanation for your results is:
foo( {1,2,3} );
You cannot deduce a template argument from a braced-init-list, so deduction fails for foo(T&&) and foo(initializer_list<int>) is the only viable function.
foo( il );
foo(initializer_list<T>) is more specialized than foo(T&&) so is chosen by overload resolution.
goo( {1,2,3} );
You cannot deduce a template argument from a braced-init-list, so goo(initializer_list<int>) is the only viable function.
goo( il );
il is a non-const lvalue, goo(T&&) can be called with T deduced as initializer_list<int>&, so its signature is goo(initializer_list<int>&) which is a better match than goo(initializer_list<int> const&) because binding the non-const il to a const-reference is a worse conversion sequence than binding it to a non-const-reference.
One of the comments above quotes Scott's slides as saying: "Makes no sense: URefs handle everything." That's true, and that's exactly why you might want to overload! You might want a more specific function for certain types, and the universal reference function for everything else. You can also use SFINAE to constrain the universal reference function to stop it handling certain types, so that other overloads can handle them.
For an example in the standard library, std::async is an overloaded function taking universal references. One overload handles the case where the first argument is of type std::launch and the other overload handles everything else. SFINAE prevents the "everything else" overload from greedily matching calls that pass std::launch as the first argument.
Ok, so first the reaction to foo makes sense. initializer_list<T> match both calls and is more specialized, therefore should be called this way.
For goo, this is in sync with perfect forwarding. when calling goo(il), there is the choice between goo(T&&) (with T = initializer_list<T>&) and the constant reference version. I guess calling the version with the non-const reference has precedence over the more specialized version with the const reference. That being said, I am not sure this is a well defined situation w.r.t. the standard.
Edit:
Note that if there were no template, that would be resolved by the paragraph 13.3.3.2 (Ranking implicit conversion sequences) of the standard. The problem here is that, AFAIK, the partial ordering of template function would dictate the second (more specialized) goo(initializer_list<T> const&) is to be called, but the ranking on implicit conversion sequences would dictate that goo(T&&) is to be called. So I guess this is an ambiguous case.