Reputation: 114461
I'm having a problem with a part of a larger program where something that I'd say is not ambiguous is considered ambiguous by both g++
and clang++
#include <functional>
#include <string>
struct Foo {
Foo(int) {}
Foo(std::string) {}
operator int () const { return 42; }
operator std::string () const { return ""; }
void foo(std::function<void(Foo&, int)>f);
void foo(std::function<void(const Foo&, int)>f) const; // xxx
void foo(std::function<void(const std::string&, Foo&)>f);
void foo(std::function<void(const std::string&, const Foo&)>f) const;
void bar() const {
this->foo([](const Foo&, int){}); // xxx
}
};
I'd expect the invocation of ::foo
made in bar
to be unambiguously resolved to the const version marked with xxx
, while instead both compilers complain that the overload resolution is ambiguous:
g++ -std=c++11 -c -Wall amb.cpp
amb.cpp: In member function ‘void Foo::bar() const’:
amb.cpp:18:40: error: call of overloaded ‘foo(Foo::bar() const::<lambda(const Foo&, int)>)’ is ambiguous
this->foo([](const Foo&, int){});
^
amb.cpp:12:10: note: candidate: void Foo::foo(std::function<void(const Foo&, int)>) const
void foo(std::function<void(const Foo&, int)>f) const;
^
amb.cpp:15:10: note: candidate: void Foo::foo(std::function<void(const std::basic_string<char>&, const Foo&)>) const
void foo(std::function<void(const std::string&, const Foo&)>f) const;
^
Why it's not clear which version I want to call? How can I work around this problem?
Upvotes: 2
Views: 1747
Reputation: 114461
The problem is that some type information is lost when passing from a lambda to an std::function
object (in particular the argument types).
To be more specific for example a function accepting std::function<void(int)>
and another accepting std::function<void(double)>
are considered equally good overloads for a void
lambda accepting an int
argument.
For example with
#include <functional>
void foo(std::function<void(int)>f);
void foo(std::function<void(double)>f);
void bar() {
foo([](int){});
}
the call in bar
is considered ambiguous
amb2.cpp: In function ‘void bar()’:
amb2.cpp:8:18: error: call of overloaded ‘foo(bar()::<lambda(int)>)’ is ambiguous
foo([](int){});
^
amb2.cpp:4:6: note: candidate: void foo(std::function<void(int)>)
void foo(std::function<void(int)>f);
^
amb2.cpp:5:6: note: candidate: void foo(std::function<void(double)>)
void foo(std::function<void(double)>f);
^
A solution is to explicitly create the proper std::function
object with:
this->foo(std::function<void(const Foo&, int)>([](const Foo&, int){}));
Upvotes: 2
Reputation: 409146
You could make the constructor taking the std::string
argument explicit
. That way the compiler can't make an implicit conversion from std::string
to Foo
.
Upvotes: 0