您的位置 首页 > 数码极客

c++如何申明template<classt>类型

语言特性

提案

Allow lambda capture [=, this]

P0409R2

Familiar template syntax for generic lambdas

P0428R2

Simplifying implicit lambda capture

P0588R1

Default constructible and assignable stateless lambdas

P0624R2

Lambdas in unevaluated contexts

P0315R4

Allow pack expansion in lambda init-capture

P0780R2

P2095R0

Deprecate implicit capture of this via [=]

P0806R2


lambda捕获 [=, this]

当隐式捕获时(使用[=]),它总是通过引用捕获。为了消除这种混淆,C++20弃用这种行为,允许更明确的[=,this],但[&]保持不变

捕获 是零或更多捕获符的逗号分隔列表,可选地以 默认捕获符 开始。仅有的默认捕获符是

  • &(以引用隐式捕获被使用的自动变量)
  • = (以复制隐式捕获被使用的自动变量)
#include <iostream> #include <type_traits> struct LT{ void f(){ [=]{ std::cout << typeid(this).name() << this->val << std::endl; }(); //从 C++20弃用, 传引用捕获 this } void g(){ [=, *this]{ std::cout << typeid(this).name() << this->val << std::endl; }(); // 从 C++17, 传值捕获 this } void h(){ [=, this]{ std::cout << typeid(this).name() << this->val << std::endl; }(); //从 C++20, 传引用捕获 this } int val = 1; }; int main() { LT lt; lt.f(); lt.g(); lt.h(); return 0; }

在线编译运行


通用lambda的模板参数列表

C++20允许使用熟悉的模板函数语法直接引入类型。

#include <iostream> #include <type_traits> #include <vector> // 完美转发 template <typename... T> void print(T &&... t) { (std::cout << ... << t) << std::endl; } int main() { std::vector<int> ivec = {0, 1, 2, 3, 4, 5}; // lambda 期望 std::vector<T> // 在 C++20前 [](auto vec){ using T = typename decltype(vec)::value_type; for(auto& v : vec) { T t = v; std::cout << t << std::endl; } }(ivec); // 从 C++20后 []<typename T>(std::vector<T> vec){ for(auto& v : vec) { std::cout << v << std::endl; } }(ivec); // 使用参数类型 // 在 C++20前 [](const auto& x){ using T = std::decay_t<decltype(x)>; T copy = x; using Iterator = typename T::const_iterator; Iterator iter = x.cbegin(); std::cout << *iter << std::endl; }(ivec); // 从 C++20后 []<typename T>(const T& x){ T copy = x; using Iterator = typename T::const_iterator; Iterator iter = x.cbegin(); std::cout << *iter << std::endl; }(ivec); // 完美转发 // 在 C++20前 [](auto&&... args){ print(std::forward<decltype(args)>(args)...); }(1, 2.2, 'c'); // 从 C++20后 []<typename... Ts>(Ts&&... args){ print(std::forward<Ts>(args)...); }(1, 2.2, 'c'); // 混合auto和T []<typename T>(const T& a, auto b){ std::cout << a << b << std::endl; }(1,"hello"); return 0; }

在未计算的上下文中

Lambda表达式可以在未计算的上下文中使用,如sizeof()、typeid()、decltype()等。主要的原则是lambdas有一个唯一的未知类型,两个lambdas及其类型永远不相等。

// 以下模板是两个不同的声明 template<class T> void f(decltype([]{}) (*s)[sizeof(T)]); template<class T> void f(decltype([]{}) (*s)[sizeof(T)]);

在下面的例子中,f()在两个翻译单元中增加同一个计数器,因为内联函数的行为就好像它只有一个定义。然而,g_s违反了ODR,因为尽管它只有一个定义,但仍然有多个不同的声明,因为在a.cpp和b.cpp中有两个不同的lambdas,因此,S有不同的非类型模板参数.

a.h

template<typename T> int counter(){ static int value{0}; return ++value; } inline int f(){ return counter<decltype([]{})>(); } template<auto> struct S{ int call(){static int value{0};return ++value;} }; // cast lambda to pointer inline S<+[]{}> g_s;

b.cpp

#include <iostream> #include "a.h" void func(){ auto v = f(); std::cout << "f:" << v << std::endl; int gv = g_s.call(); std::cout << "g:" << gv << std::endl; }

a.cpp

#include <iostream> #include "a.h" void func(); int main() { auto v = f(); std::cout << "f:" << v << std::endl; int gv = g_s.call(); std::cout << "g:" << gv << std::endl; func(); return 0; }

在线运行测试

运行结果:

f:1 g:1 f:2 g:1

默认的可构造和可赋值的无状态lambda

在C++20中,无状态lambda是默认可构造和可赋值的,在未求值上下文中,我们可以通过decltype()获得lambda的类型,并在稍后创建该类型的变量。

例子中,std::map接受一个比较器类型,以便稍后实例化它。虽然我们可以在C++17中获得一个lambda类型,但无法实例化它,因为lambdas不是默认可构造的。

#include <iostream> #include <type_traits> #include <map> int main() { auto greater = [](auto x,auto y){ std::cout << x << y << std::endl; return x > y; }; // 需要是默认可构造的类型 std::map<std::string, int, decltype(greater)> map1; // 需要是默认可赋值的类型 auto map2 = map1; map2["1"] = 1; map2["2"] = 2; map2["3"] = 3; return 0; }

运行结果:

12 21 21 13 23 32 32

在lambda捕获中进行参数包展开

C++20简化了lambda中的参数包捕获。在C++20之前,如果我们想移动包,可以通过值、引用或std::tuple来捕获它们。现在就简单多了,我们可以在初始化捕获中直接捕获参数包。它并不局限于std::move或std::forward,任何函数都可以应用于参数包元素。

#include <iostream> #include <type_traits> #include <map> void f(double, double){std::cout << "fd" << std::endl;} void g(int, int,double){std::cout << "gi" << std::endl;} // C++17 template<class F, class... Args> auto delay_apply(F&& f, Args&&... args) { return [f=std::forward<F>(f), tup=std::make_tuple(std::forward<Args>(args)...)]() -> decltype(auto) { return std::apply(f, tup); }; } // C++20 template<typename F, typename... Args> auto delay_call(F&& f, Args&&... args) { return [f = std::forward<F>(f), ...f_args=std::forward<Args>(args)]() -> decltype(auto) { return f(f_args...); }; } int main() { delay_apply(f, 1.1, 2.2)(); delay_call(g, 1, 2, 3.3)(); return 0; }

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“c++如何申明template<classt>类型,如何申明函数,儿童票如何申明,如何写申明要款”边界阅读