I am trying to write a type trait that checks whether the types stored in a tuple are compatible with the arguments of a given callable.
Currently, I have 'almost working' code, shown below. However, static assert fails in the last statement with a callable that expects reference parameters (e.g. [](int&, std::string&){}
), I don't really understand why it's failing. And how does one write a trait that is inclusive for this type as well?
#include <type_traits>
#include <tuple>
#include <string>
template<typename, typename>
struct is_callable_with_tuple: std::false_type {};
template<typename Func, template<typename...> class Tuple, typename... Args>
struct is_callable_with_tuple<Func, Tuple<Args...>>: std::is_invocable<Func, Args...> {};
template<typename Func, typename Args>
constexpr bool is_callable_with_tuple_v = is_callable_with_tuple<Func, Args>::value;
int main() {
static_assert(is_callable_with_tuple_v<decltype([](int, std::string){}), std::tuple<int, std::string>>); // OK
static_assert(is_callable_with_tuple_v<decltype([](const int&, const std::string&){}), std::tuple<int, std::string>>); // OK
static_assert(is_callable_with_tuple_v<decltype([](int&, std::string&){}), std::tuple<int, std::string>>); // Fails
}
CodePudding user response:
You may want to modify your trait slightly:
template<typename Func,
template<typename...> class Tuple,
typename... Args>
struct is_callable_with_tuple<Func, Tuple<Args...>>:
std::is_invocable<Func, Args&...> {}; // <--- note &
Or not, depending on how exactly you plan to use it. If your tuple is always an lvalue, it is probably OK. If not, then you may want to specialise it for an lvalue-reference tuple type:
template<typename Func,
template<typename...> class Tuple,
typename... Args>
struct is_callable_with_tuple<Func, Tuple<Args...>&>: // <--- note & here
std::is_invocable<Func, Args&...> {}; // <--- and also here
template<typename Func,
template<typename...> class Tuple,
typename... Args>
struct is_callable_with_tuple<Func, Tuple<Args...>>: // <--- note NO & here
std::is_invocable<Func, Args...> {}; // <--- and also here
and use it like this:
is_callable_with_tuple<decltype(myfunc), decltype((mytuple))> // note double parentheses