https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81211

--- Comment #4 from Jonathan Wakely <redi at gcc dot gnu.org> ---
That's not really very practical. std::function uses std::result_of to check
for a callable type, and it's not an error for std::result_of to decide the
type isn't callable. The error happens when std::function requests the
std::result_of<>::type member, which doesn't exist. But the reason for it not
existing is long gone by that point, decided earlier in the internals of
std::result_of and not available to the compiler.

Using decltype(std::declval<_Func&>()(std::declval<_ArgTypes>()...)) instead of
result_of gives a nice diagnostic:

/home/jwakely/gcc/8/include/c++/8.0.0/bits/std_function.h:465:2: note:
candidate: ‘template<class _Functor, class, class> std::function<_Res(_ArgTypes
...)>::function(_Functor)’
  function(_Functor);
  ^~~~~~~~
/home/jwakely/gcc/8/include/c++/8.0.0/bits/std_function.h:465:2: note:  
template argument deduction/substitution failed:
/home/jwakely/gcc/8/include/c++/8.0.0/bits/std_function.h:394:55: error: use of
deleted function ‘constexpr T::T(const T&)’
         typename _Res2 =
decltype(std::declval<_Func&>()(std::declval<_ArgTypes>()...))>
                                   ~~~~~~~~~~~~~~~~~~~~^~
func.cc:2:8: note: ‘constexpr T::T(const T&)’ is implicitly declared as deleted
because ‘T’ declares a move constructor or move assignment operator
 struct T
        ^
func.cc:16:33: note:   initializing argument 1 of ‘main()::<lambda(T)>’
   std::function<void(T)> f{ [](T) { } };
                                 ^

But using decltype there is incorrect, it wouldn't work for pointers to
members. Which is why we use result_of.

We can't add something like:

static_assert( __and_<is_move_constructible<ArgTypes>...>::value,
               "arguments can be passed to the target function" );

Because that would reject this valid code:

#include <functional>

struct T
{
        T(int) {}
        // uncomment one of the  below to make the code compile
        //T(const T&) {}
        //T(T&& t) {}
        T& operator=(const T&) { return *this; }
        T& operator=(T&&) { return *this; }
};

int main()
{
        std::function<void(T)> f{ [](T&&) {} };
        f( {1} );
}

I don't see anything that can really be done to improve things here.

(In reply to Stephen Kell from comment #3)
> It'd be nice if I didn't have to guess that the reason it's not callable is
> that I omitted the copy constructor. The rules for when constructors are
> defaulted/deleted are baroque enough to catch anybody out from time to time.

Not if you follow the rule of five:
http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c21-if-you-define-or-delete-any-default-operation-define-or-delete-them-all

Your class has user-provided assignment operators and fails to declare copy and
move constructors. Define them (maybe as =default) and there's no problem.

Reply via email to