Ric*_*ter 14 c++ lambda template-meta-programming generic-lambda c++14
我需要知道lambda具有的确切参数数量.我不关心他们的类型,我只需要一个计数.
auto lambda0 = [&]() { ... };
auto lambda1 = [&](int32_t a) { ... };
auto lambda2 = [&](int32_t a, auto b) { ... };
lambda_details<decltype(lambda0)>::argument_count; // Equals 0
lambda_details<decltype(lambda1)>::argument_count; // Equals 1
lambda_details<decltype(lambda2)>::argument_count; // Equals 2
Run Code Online (Sandbox Code Playgroud)
检测变量lambda也很好,所以我也可以处理那个边缘情况.
auto lambda_variadic = [&](auto... args){ ... };
lambda_details<decltype(lambda_variadic)>::is_variadic; // Equals true
Run Code Online (Sandbox Code Playgroud)
我怎样才能获得这些信息?
您可以通过重载转换运算符来创建可以进入任何参数的对象.从那里只测试lambda是否可以用给定数量的这样的参数调用,从一些任意大数字倒数.如果lambda在第一次尝试时恰好是可调用的(给定任意大量的参数),我们可以假设它是可变的:
#include <iostream>
#include <utility>
#include <type_traits>
struct any_argument {
template <typename T>
operator T&&() const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl
: std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl<Lambda, std::index_sequence<Is...>,
decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())>
: std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept
: can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t Max, std::size_t N, typename = void>
struct lambda_details_impl
: lambda_details_impl<Lambda, Max, N - 1>
{};
template <typename Lambda, std::size_t Max, std::size_t N>
struct lambda_details_impl<Lambda, Max, N, std::enable_if_t<can_accept<Lambda, N>::value>>
{
static constexpr bool is_variadic = (N == Max);
static constexpr std::size_t argument_count = N;
};
template <typename Lambda, std::size_t Max = 50>
struct lambda_details
: lambda_details_impl<Lambda, Max, Max>
{};
int main()
{
auto lambda0 = []() {};
auto lambda1 = [](int a) {};
auto lambda2 = [](int a, auto b) {};
auto lambda3 = [](int a, auto b, char = 'a') {};
auto lambda4 = [](int a, auto b, char = 'a', auto...) {};
std::cout << lambda_details<decltype(lambda0)>::is_variadic << " " << lambda_details<decltype(lambda0)>::argument_count << "\n"; // 0 0
std::cout << lambda_details<decltype(lambda1)>::is_variadic << " " << lambda_details<decltype(lambda1)>::argument_count << "\n"; // 0 1
std::cout << lambda_details<decltype(lambda2)>::is_variadic << " " << lambda_details<decltype(lambda2)>::argument_count << "\n"; // 0 2
std::cout << lambda_details<decltype(lambda3)>::is_variadic << " " << lambda_details<decltype(lambda3)>::argument_count << "\n"; // 0 3
std::cout << lambda_details<decltype(lambda4)>::is_variadic << " " << lambda_details<decltype(lambda4)>::argument_count << "\n"; // 1 50
}
Run Code Online (Sandbox Code Playgroud)
我已经使用 @yuri kilochek 答案的修改版本解决了这个问题。
我们不是从 50 个参数开始递减计数,而是从零开始递增计数。当我们获得匹配时,我们就知道调用 lambda 所需的最小参数数量。然后,我们继续搜索,直到达到合理的最大值,以查看是否存在最大数量的参数(当您有默认参数时可能会发生这种情况)。
如果达到参数计数限制,我们假设 lambda 是可变参数。
此实现显着减少了非可变参数 lambda 的模板实例化数量。它还为我们提供了所有 lambda 的最小参数数量,以及任何非可变参数 lambda 的最大参数数量。
再次非常感谢 Yuri Kilochek 为这个优雅的解决方案奠定了基础。检查他的答案以获取有关实施的更多详细信息。
struct any_argument
{
template <typename T>
operator T && () const;
};
template <typename Lambda, typename Is, typename = void>
struct can_accept_impl : std::false_type
{};
template <typename Lambda, std::size_t ...Is>
struct can_accept_impl <Lambda, std::index_sequence<Is...>, decltype(std::declval<Lambda>()(((void)Is, any_argument{})...), void())> : std::true_type
{};
template <typename Lambda, std::size_t N>
struct can_accept : can_accept_impl<Lambda, std::make_index_sequence<N>>
{};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_maximum
{
static constexpr size_t maximum_argument_count = N - 1;
static constexpr bool is_variadic = false;
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N <= Max)>> : lambda_details_maximum<Lambda, N + 1, Max>
{};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_maximum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value && (N > Max)>>
{
static constexpr bool is_variadic = true;
};
template <typename Lambda, std::size_t N, size_t Max, typename = void>
struct lambda_details_minimum : lambda_details_minimum<Lambda, N + 1, Max>
{
static_assert(N <= Max, "Argument limit reached");
};
template <typename Lambda, std::size_t N, size_t Max>
struct lambda_details_minimum<Lambda, N, Max, std::enable_if_t<can_accept<Lambda, N>::value>> : lambda_details_maximum<Lambda, N, Max>
{
static constexpr size_t minimum_argument_count = N;
};
template <typename Lambda, size_t Max = 50>
struct lambda_details : lambda_details_minimum<Lambda, 0, Max>
{};
Run Code Online (Sandbox Code Playgroud)
另一件需要注意的重要事情是,这any_argument不会自动与操作员配合良好。auto如果您希望它与所操作的参数一起使用(例如 ),则必须重载每一个[](auto a) { return a * 2; }。它最终会看起来更像这样:
struct any_argument
{
template <typename T> operator T && () const;
any_argument& operator ++();
any_argument& operator ++(int);
any_argument& operator --();
any_argument& operator --(int);
template <typename T> friend any_argument operator + (const any_argument&, const T&);
template <typename T> friend any_argument operator + (const T&, const any_argument&);
template <typename T> friend any_argument operator - (const any_argument&, const T&);
template <typename T> friend any_argument operator - (const T&, const any_argument&);
template <typename T> friend any_argument operator * (const any_argument&, const T&);
template <typename T> friend any_argument operator * (const T&, const any_argument&);
template <typename T> friend any_argument operator / (const any_argument&, const T&);
template <typename T> friend any_argument operator / (const T&, const any_argument&);
// And every other operator in existence
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
461 次 |
| 最近记录: |