Checking template template types

EDIT: . The approach outlined in the question is problematic for several reasons. In the end, I decided this by going differently, see my answer below.

I have several template classes in which it is expected that the template parameter will map to a specific signature. If the user supplies a template argument that is either not callable or does not match the expected signature, then compilation fails inside the callback mechanism, and the resulting error messages are very difficult to decrypt. Instead, I would like to be able to use static_assertit to provide a good, easy-to-understand error message up if this template parameter is invalid. Unfortunately, this is quite difficult to do.

I use the following setting:

#include <type_traits>

namespace detail {

template <typename Function, typename Sig>
struct check_function
{
    static constexpr bool value =
        std::is_convertible<Function, typename std::decay<Sig>::type>::value;
};

template <typename, typename>
struct check_functor;

template <typename Functor, typename Ret, typename... Args>
struct check_functor<Functor, Ret(Args...)>
{
    typedef Ret (Functor::*Memfn) (Args...);

    static constexpr bool value =
        check_function<decltype(&Functor::operator()), Memfn>::value;
};

} // end namespace detail

template <typename Func, typename Sig>
struct check_function_signature
{
    using Type =
        typename std::conditional<
                     std::is_function<Func>::value,
                     detail::check_function<Func, Sig>,
                     detail::check_functor<Func, Sig>>::type;

    static constexpr bool value = Type::value;
};

. Func - , , , operator().

, , , - , lambdas ( Clang 3.4 g++ 4.8):

int f(int i);

struct g
{
    int operator() (int i) { return i; }
};

int main()
{
    static_assert(check_function_signature<decltype(f), int(int)>::value,
                  "Fine");

    static_assert(check_function_signature<g, int(int)>::value,
                  "Also fine");

    auto l = [](int i) { return i; };
    static_assert(check_function_signature<decltype(l), int(int)>::value,
                  "Fails");
}

, , , g , , , .

, , :

  • , , , ?
  • , (.. lambdas)?
  • / , ?
  • - , ? (, ...)

, , .

+4
2

( , , , , .)

, , remaybel, , operator() . , T::operator() , , operator() . , , , .

, , , , . , :

template <typename, typename, typename = void>
struct check_signature : std::false_type {};

template <typename Func, typename Ret, typename... Args>
struct check_signature<Func, Ret(Args...),
    typename std::enable_if<
        std::is_convertible<
            decltype(std::declval<Func>()(std::declval<Args>()...)),
            Ret
        >::value, void>::type>
    : std::true_type {};

"dummy" declval , . , SFINAE , .

() , , . , .

, , , , - , , , .

+2

const operator ().

template <typename Functor, typename Ret, typename... Args>
struct check_functor<Functor, Ret(Args...)>
{
    typedef Ret (Functor::*Memfn) (Args...) const; // const added here

    static constexpr bool value =
        check_function<decltype(&Functor::operator()), Memfn>::value;
};

( ) ( ). :

auto l = [](int i) mutable { return i; };
+1

All Articles