将模板函数转换为通用 lambda
Convert template function to generic lambda
我想传递模板函数,就好像它们是通用的 lambda 表达式一样,但这行不通。
#include <iostream>
#include <vector>
#include <tuple>
#include <string>
#include <utility>
// for_each with std::tuple
// (from
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT)
{}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f) {
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
// my code
template<class T> auto
print(const std::vector<T>& v) -> void {
for (const auto& e : v) {
std::cout << e << "\t";
}
}
struct print_wrapper {
template<class T>
auto operator()(const std::vector<T>& v) {
print(v);
}
};
auto print_gen_lambda = [](const auto& v){ print(v); };
auto print_gen_lambda_2 = []<class T>(const std::vector<T>& v){ print(v); }; // proposal P0428R1, gcc extension in c++14/c++17
int main() {
std::tuple<std::vector<int>,std::vector<double>,std::vector<std::string>> t = { {42,43},{3.14,2.7},{"Hello","World"}};
for_each(t, print); // case 1: error: template argument deduction/substitution failed: couldn't deduce template parameter 'FuncT'
for_each(t, print_wrapper()); // case 2: ok
for_each(t, print_gen_lambda); // case 3: ok
for_each(t, print_gen_lambda_2); // case 4: ok
}
请注意,情况 2 和情况 4 是严格等效的。案例 3 更一般但不受约束(这对我来说是个问题)。我认为案例 1 应该被语言视为等同于案例 2 和 4,但事实并非如此。
- 是否有人提议将模板函数隐式转换为通用约束 lambda(案例 2/4)?如果不是,是否存在阻止这样做的基本语言原因?
- 现在只能用案例2了,比较麻烦。
- 情况 4:不符合 c++14,即使在 c++20 中应该是标准的,但仍然不完美(冗长,因为您创建的 lambda 基本上不添加任何信息)。
- 案例 3:不受约束,但我依赖于(此处未显示)使用非 "vector" 参数调用 "print" 的替换失败(P0428R1 提到了这个问题)。所以我想附属问题是 "Can I constrain a generic lambda with some enable_if tricks?"
在 C++14/17/20 中,是否有一种非常简洁的方式来实现从案例 1 到案例 2 的转换?我什至对宏黑客持开放态度。
Is there, in C++14/17/20, a very terse manner to enable the conversion from case 1 to case 2? I am even open to macro hacks.
是的。
// C++ requires you to type out the same function body three times to obtain
// SFINAE-friendliness and noexcept-correctness. That's unacceptable.
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__){ return __VA_ARGS__; }
// The name of overload sets can be legally used as part of a function call -
// we can use a macro to create a lambda for us that "lifts" the overload set
// into a function object.
#define LIFT(f) [](auto&&... xs) RETURNS(f(::std::forward<decltype(xs)>(xs)...))
然后你可以说:
for_each(t, LIFT(print));
Is there a proposal to implicitly convert a template function to a generic constrained lambda?
Can I constrain a generic lambda with some enable_if tricks?
如果你想要的只是限制通用 lambda 的参数类型,你可以用几个函数声明(不需要定义)和一个 static_assert
(这样你就可以得到在失败的情况下在编译时出现一个优雅的消息错误)。周围根本没有宏(它们是如此 C-ish)。
它遵循一个最小的工作示例:
#include<vector>
#include<type_traits>
#include<utility>
#include<list>
template<template<typename...> class C, typename... A>
constexpr std::true_type spec(int, C<A...>);
template<template<typename...> class C, template<typename...> class T, typename... A>
constexpr std::false_type spec(char, T<A...>);
int main() {
auto fn = [](auto&& v) {
static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");
// ...
};
fn(std::vector<int>{});
// fn(std::list<int>{});
//fn(int{});
}
如果将注释切换到最后几行,static_assert
将引发错误并且编译将按预期失败。
在 wandbox.
上查看并 运行
旁注。
当然你可以在这里减少样板文件:
static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");
添加如下所示的变量模板:
template<template<typename...> class C, typename T>
constexpr bool match = decltype(spec<C>(0, std::declval<std::decay_t<T>>()))::value;
然后在您的 static_assert
中使用它:
static_assert(match<std::vector, decltype(v)>, "!");
很清楚,不是吗?
备注。
在 C++17 中,您可以通过将 lambda 定义为:
来进一步减少执行此操作所需的代码
auto fn = [](auto&& v) {
if constexpr(match<std::vector, decltype(v)>) {
print(v);
}
};
请参阅 wandbox 上的示例代码 运行。
我想传递模板函数,就好像它们是通用的 lambda 表达式一样,但这行不通。
#include <iostream>
#include <vector>
#include <tuple>
#include <string>
#include <utility>
// for_each with std::tuple
// (from
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I == sizeof...(Tp), void>::type
for_each(std::tuple<Tp...> &, FuncT)
{}
template<std::size_t I = 0, typename FuncT, typename... Tp>
inline typename std::enable_if<I < sizeof...(Tp), void>::type
for_each(std::tuple<Tp...>& t, FuncT f) {
f(std::get<I>(t));
for_each<I + 1, FuncT, Tp...>(t, f);
}
// my code
template<class T> auto
print(const std::vector<T>& v) -> void {
for (const auto& e : v) {
std::cout << e << "\t";
}
}
struct print_wrapper {
template<class T>
auto operator()(const std::vector<T>& v) {
print(v);
}
};
auto print_gen_lambda = [](const auto& v){ print(v); };
auto print_gen_lambda_2 = []<class T>(const std::vector<T>& v){ print(v); }; // proposal P0428R1, gcc extension in c++14/c++17
int main() {
std::tuple<std::vector<int>,std::vector<double>,std::vector<std::string>> t = { {42,43},{3.14,2.7},{"Hello","World"}};
for_each(t, print); // case 1: error: template argument deduction/substitution failed: couldn't deduce template parameter 'FuncT'
for_each(t, print_wrapper()); // case 2: ok
for_each(t, print_gen_lambda); // case 3: ok
for_each(t, print_gen_lambda_2); // case 4: ok
}
请注意,情况 2 和情况 4 是严格等效的。案例 3 更一般但不受约束(这对我来说是个问题)。我认为案例 1 应该被语言视为等同于案例 2 和 4,但事实并非如此。
- 是否有人提议将模板函数隐式转换为通用约束 lambda(案例 2/4)?如果不是,是否存在阻止这样做的基本语言原因?
- 现在只能用案例2了,比较麻烦。
- 情况 4:不符合 c++14,即使在 c++20 中应该是标准的,但仍然不完美(冗长,因为您创建的 lambda 基本上不添加任何信息)。
- 案例 3:不受约束,但我依赖于(此处未显示)使用非 "vector" 参数调用 "print" 的替换失败(P0428R1 提到了这个问题)。所以我想附属问题是 "Can I constrain a generic lambda with some enable_if tricks?"
在 C++14/17/20 中,是否有一种非常简洁的方式来实现从案例 1 到案例 2 的转换?我什至对宏黑客持开放态度。
Is there, in C++14/17/20, a very terse manner to enable the conversion from case 1 to case 2? I am even open to macro hacks.
是的。
// C++ requires you to type out the same function body three times to obtain
// SFINAE-friendliness and noexcept-correctness. That's unacceptable.
#define RETURNS(...) noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__){ return __VA_ARGS__; }
// The name of overload sets can be legally used as part of a function call -
// we can use a macro to create a lambda for us that "lifts" the overload set
// into a function object.
#define LIFT(f) [](auto&&... xs) RETURNS(f(::std::forward<decltype(xs)>(xs)...))
然后你可以说:
for_each(t, LIFT(print));
Is there a proposal to implicitly convert a template function to a generic constrained lambda?
Can I constrain a generic lambda with some enable_if tricks?
如果你想要的只是限制通用 lambda 的参数类型,你可以用几个函数声明(不需要定义)和一个 static_assert
(这样你就可以得到在失败的情况下在编译时出现一个优雅的消息错误)。周围根本没有宏(它们是如此 C-ish)。
它遵循一个最小的工作示例:
#include<vector>
#include<type_traits>
#include<utility>
#include<list>
template<template<typename...> class C, typename... A>
constexpr std::true_type spec(int, C<A...>);
template<template<typename...> class C, template<typename...> class T, typename... A>
constexpr std::false_type spec(char, T<A...>);
int main() {
auto fn = [](auto&& v) {
static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");
// ...
};
fn(std::vector<int>{});
// fn(std::list<int>{});
//fn(int{});
}
如果将注释切换到最后几行,static_assert
将引发错误并且编译将按预期失败。
在 wandbox.
旁注。
当然你可以在这里减少样板文件:
static_assert(decltype(spec<std::vector>(0, std::declval<std::decay_t<decltype(v)>>()))::value, "!");
添加如下所示的变量模板:
template<template<typename...> class C, typename T>
constexpr bool match = decltype(spec<C>(0, std::declval<std::decay_t<T>>()))::value;
然后在您的 static_assert
中使用它:
static_assert(match<std::vector, decltype(v)>, "!");
很清楚,不是吗?
备注。
在 C++17 中,您可以通过将 lambda 定义为:
来进一步减少执行此操作所需的代码auto fn = [](auto&& v) {
if constexpr(match<std::vector, decltype(v)>) {
print(v);
}
};
请参阅 wandbox 上的示例代码 运行。