c++ - Overload resolution with std::function -
consider example of code:
#include <iostream> #include <functional> typedef std::function<void()> func1_t; typedef std::function<void(int)> func2_t; struct x { x (func1_t f) { } x (func2_t f) { } }; int main ( ) { x x([](){ std::cout << "hello, world!\n"; }); } i sure shouldn't compile, because compiler shouldn't able choose 1 of 2 constructors. g++-4.7.3 shows expected behavior: says call of overloaded constructor ambiguous. however, g++-4.8.2 compiles it.
is code correct in c++11 or bug/feature of version of g++?
in c++11...
let's take @ specification of constructor template of std::function (which takes callable): [func.wrap.func.con]/7-10
template<class f> function(f f); template <class f, class a> function(allocator_arg_t, const a& a, f f);7 requires:
fshallcopyconstructible.fshallcallable(20.10.11.2) argument typesargtypes, return typer. copy constructor , destructor ofashall not throw exceptions.8 postconditions:
!*thisif of following hold:
fnullfunction pointer.fnullpointer member.finstance of function class template, ,!f9 otherwise,
*thistargets copy offinitializedstd::move(f). [left out note here]10 throws: shall not throw exceptions when
ffunction pointer orreference_wrapper<t>t. otherwise, may throwbad_allocor exception thrownf’s copy or move constructor.
now, constructing, or attempting construct (for overload resolution) std::function<void(int)> [](){} (i.e. signature void(void)) violates requirements of std::function<void(int)>'s constructor.
[res.on.required]/1
violation of preconditions specified in function’s requires: paragraph results in undefined behavior unless function’s throws: paragraph specifies throwing exception when precondition violated.
so, afaik, result of overload resolution undefined. therefore, both versions of g++/libstdc++ complying in aspect.
in c++14, has been changed, see lwg 2132. now, converting constructor template of std::function required sfinae-reject incompatible callables (more sfinae in next chapter):
template<class f> function(f f); template <class f, class a> function(allocator_arg_t, const a& a, f f);7 requires:
fshallcopyconstructible.8 remarks: these constructors shall not participate in overload resolution unless
fcallable (20.9.11.2) argument typesargtypes..., return typer.[...]
the "shall not participate in overload resolution" corresponds rejection via sfinae. net effect if have overload set of functions foo,
void foo(std::function<void(double)>); void foo(std::function<void(char const*)>); and call-expression such as
foo([](std::string){}) // (c) then second overload of foo chosen unambiguously: since std::function<f> defines f interface outside, f defines argument types passed std::function. then, wrapped function object has called arguments (argument types). if double passed std::function, cannot passed on function taking std::string, because there's no conversion double -> std::string. first overload of foo, argument [](std::string){} therefore not considered callable std::function<void(double)>. constructor template deactivated, hence there's no viable conversion [](std::string){} std::function<void(double)>. first overload removed overload set resolving call (c), leaving second overload.
note there's been slight change wording above, due lwg 2420: there's exception if return type r of std::function<r(argtypes...)> void, return type accepted (and discarded) callable in constructor template mentioned above. example, both []() -> void {} , []() -> bool {} callable std::function<void()>. following situation therefore produces ambiguity:
void foo(std::function<void()>); void foo(std::function<bool()>); foo([]() -> bool {}); // ambiguous the overload resolution rules don't try rank among different user-defined conversions, , hence both overloads of foo viable (first of all) , neither better.
how can sfinae here?
note when sfinae-check fails, program isn't ill-formed, function isn't viable overload resolution. example:
#include <type_traits> #include <iostream> template<class t> auto foo(t) -> typename std::enable_if< std::is_integral<t>::value >::type { std::cout << "foo 1\n"; } template<class t> auto foo(t) -> typename std::enable_if< not std::is_integral<t>::value >::type { std::cout << "foo 2\n"; } int main() { foo(42); foo(42.); } similarly, conversion can made non-viable using sfinae on converting constructor:
#include <type_traits> #include <iostream> struct foo { template<class t, class = typename std::enable_if< std::is_integral<t>::value >::type > foo(t) { std::cout << "foo(t)\n"; } }; struct bar { template<class t, class = typename std::enable_if< not std::is_integral<t>::value >::type > bar(t) { std::cout << "bar(t)\n"; } }; struct kitty { kitty(foo) {} kitty(bar) {} }; int main() { kitty cat(42); kitty tac(42.); }
Comments
Post a Comment