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.
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:
and your usage becomes:
The arguments do not exactly match either call:
fooisFoo&, notconst Foo&v.crbegin()returnvector::const_reverse_iteratorwhich is just a different instantiation of the samestd::reverse_iteratortemplate asvector::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:I.e. there is a viable candidate converting
std::reverse_iterator<T>tostd::reverse_iterator<U>between anyT-Upairs. In this case forT=vector::const_iterator,U=vector::iterator. But of course the template instantiation fails later because it cannot convertconst int*toint*.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:
Solution
As pointed in the comments by @Eljay, forcing
const Foo&at the call site is one option, one can use C++17std::as_const: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
constmethod qualifier seems easiest to me:It works because now it is a better (exact) match for non-
constFoos than the still consideredvector::reverse_iteratorwhich would not compile anyway.