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:
f
shallcopyconstructible
.f
shallcallable
(20.10.11.2) argument typesargtypes
, return typer
. copy constructor , destructor ofa
shall not throw exceptions.8 postconditions:
!*this
if of following hold:
f
null
function pointer.f
null
pointer member.f
instance of function class template, ,!f
9 otherwise,
*this
targets copy off
initializedstd::move(f)
. [left out note here]10 throws: shall not throw exceptions when
f
function pointer orreference_wrapper<t>
t
. otherwise, may throwbad_alloc
or 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:
f
shallcopyconstructible
.8 remarks: these constructors shall not participate in overload resolution unless
f
callable (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