I have the following type trait:
我有以下类型的特点:
template <class T>
struct Arity : Arity<decltype(&T::operator())> {};
template <class T, class R, class... Args>
struct Arity<R(T::*)(Args...)> {
static constexpr auto value = sizeof...(Args);
};
template <class T, class R, class... Args>
struct Arity<R(T::*)(Args...) const> {
static constexpr auto value = sizeof...(Args);
};
template <class R, class... Args>
struct Arity<R(*)(Args...)> {
static constexpr auto value = sizeof...(Args);
};
Which works great to find the number of arguments a function takes for most use cases, but it fails for one common case:
这对于找到一个函数在大多数用例中需要的参数数量非常有用,但是对于一个常见的情况它失败了:
auto l1 = [](int, double){};
Arity<decltype(l1)>::value; // works, 2
auto l2 = [](auto, auto){};
Arity<decltype(l2)>::value; // error: Reference to overloaded function could not be resolved; did you mean to call it?
I believe it's impossible to generally make this work for any templated function / operator() because depending on the types/values passed as template types, a different overload could be selected, or perhaps no overload may be available at all. Also, there's no way to know what valid types and values to pass as template arguments. But still, I want this to work for the common case of a lambda taking auto
arguments. Is there any way to make this more robust and cover lambdas which take auto arguments?
我认为,对于任何模板化的函数/操作符()来说,通常都不可能完成这项工作,因为根据作为模板类型传递的类型/值,可以选择不同的重载,或者可能没有重载。此外,也没有办法知道将哪些有效类型和值作为模板参数传递。但是,我还是想让它适用于使用自动参数的lambda表达式。有没有办法让这个更健壮的和覆盖的小羊羔包含自动参数?
2 个解决方案
#1
4
I guess I achieved half of a solution here. Only works up to a fixed number of parameters, but for most applications that shouldn't be an issue. Also, it's probably highly simplifiable but my brain is not into tricky SFINAE right now.
我想我得到了一半的解。只适用于固定数量的参数,但对于大多数应用程序来说,这不是问题。而且,它可能是高度简化的,但我的大脑现在不喜欢棘手的SFINAE。
template <
class, std::size_t N,
class = std::make_index_sequence<N>,
class = void_t<>
>
struct CanCall : std::false_type { };
template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
F, N,
std::index_sequence<Idx...>,
void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };
CanCall<F, N>
will return whether F
is callable with N
parameters of arbitrary type. The Any
helper type has templated implicit conversion operators that allows it to morph into any desired parameter type.
CanCall
template <class F, std::size_t N = 0u, class = void>
struct Arity : Arity<F, N + 1u, void> { };
template <class F, std::size_t N>
struct Arity<F, N, std::enable_if_t<CanCall<F, N>::value>>
: std::integral_constant<std::size_t, N> { };
template <class F>
struct Arity<F, MAX_ARITY_PROBING, void>
: std::integral_constant<std::size_t, ARITY_VARIADIC> { };
Arity<F>
just checks whether an F
can be called with zero, one, two... parameters. First positive check wins. If we reach MAX_ARITY_PROBING
parameters, Arity
bails out and supposes that the function is either variadic, or is not a function at all.
在Coliru上看
#2
2
I don't think you can use lambda functions in your use case whose argument types are auto
. The operator()
functions of such lambda functions are most likely implemented using function templates.
我认为你不能在你的用例中使用lambda函数,它的参数类型是自动的。这种lambda函数的操作符()函数很可能是使用函数模板实现的。
Hence, decltype
can't be used with:
因此,decltype不能用于:
auto l2 = [](auto, auto){};
Arity<decltype(l2)>::value;
See this answer to another SO question for more on the subject.
有关这个问题的更多信息,请参阅另一个SO问题的答案。
#1
4
I guess I achieved half of a solution here. Only works up to a fixed number of parameters, but for most applications that shouldn't be an issue. Also, it's probably highly simplifiable but my brain is not into tricky SFINAE right now.
我想我得到了一半的解。只适用于固定数量的参数,但对于大多数应用程序来说,这不是问题。而且,它可能是高度简化的,但我的大脑现在不喜欢棘手的SFINAE。
template <
class, std::size_t N,
class = std::make_index_sequence<N>,
class = void_t<>
>
struct CanCall : std::false_type { };
template <class F, std::size_t N, std::size_t... Idx>
struct CanCall<
F, N,
std::index_sequence<Idx...>,
void_t<decltype(std::declval<F>()((Idx, std::declval<Any const&&>())...))>
> : std::true_type { };
CanCall<F, N>
will return whether F
is callable with N
parameters of arbitrary type. The Any
helper type has templated implicit conversion operators that allows it to morph into any desired parameter type.
CanCall
template <class F, std::size_t N = 0u, class = void>
struct Arity : Arity<F, N + 1u, void> { };
template <class F, std::size_t N>
struct Arity<F, N, std::enable_if_t<CanCall<F, N>::value>>
: std::integral_constant<std::size_t, N> { };
template <class F>
struct Arity<F, MAX_ARITY_PROBING, void>
: std::integral_constant<std::size_t, ARITY_VARIADIC> { };
Arity<F>
just checks whether an F
can be called with zero, one, two... parameters. First positive check wins. If we reach MAX_ARITY_PROBING
parameters, Arity
bails out and supposes that the function is either variadic, or is not a function at all.
在Coliru上看
#2
2
I don't think you can use lambda functions in your use case whose argument types are auto
. The operator()
functions of such lambda functions are most likely implemented using function templates.
我认为你不能在你的用例中使用lambda函数,它的参数类型是自动的。这种lambda函数的操作符()函数很可能是使用函数模板实现的。
Hence, decltype
can't be used with:
因此,decltype不能用于:
auto l2 = [](auto, auto){};
Arity<decltype(l2)>::value;
See this answer to another SO question for more on the subject.
有关这个问题的更多信息,请参阅另一个SO问题的答案。