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 shall copyconstructible. f shall callable (20.10.11.2) argument types argtypes , return type r. copy constructor , destructor of a 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 of f initialized std::move(f). [left out note here]

10 throws: shall not throw exceptions when f function pointer or reference_wrapper<t> t. otherwise, may throw bad_alloc or exception thrown f’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 shall copyconstructible.

8 remarks: these constructors shall not participate in overload resolution unless f callable (20.9.11.2) argument types argtypes... , return type r.

[...]

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

Popular posts from this blog

html - Styling progress bar with inline style -

java - Oracle Sql developer error: could not install some modules -

How to use autoclose brackets in Jupyter notebook? -