C++17中lambda表达式新增加支持的features

      C++17中对lambda表达式新增加了2种features:lambda capture of *this和constexpr lambda

      1.lambda capture of *this:

      *this:拷贝当前对象,创建副本:捕获*this意味着该lambda生成的闭包将存储当前对象的一份拷贝 。
      this:通过引用捕获
      当你需要捕获一个对象的成员变量时,不能直接去捕获成员变量。需要先去捕获对象的this指针或引用。

*this: simple by-copy capture of the current object
    [=, *this] {};   // since C++17: OK: captures the enclosing by copy
this: simple by-reference capture of the current object
    [&, this] {};    // C++11: OK, equivalent to [&]
    [&, this, i] {}; // C++11: OK, equivalent to [&, i]

      测试代码如下:

namespace {

class S {
public:
	void f()
	{
		int i{ 0 };

		auto l1 = [=] { use(i, x); }; // captures a copy of i and a copy of the this pointer
		i = 1; x = 1; l1();           // calls use(0, 1), as if i by copy and x by reference

		auto l2 = [i, this] { use(i, x); }; // same as above, made explicit
		i = 2; x = 2; l2();					// calls use(1, 2), as if i by copy and x by reference

		auto l3 = [&] { use(i, x); }; // captures i by reference and a copy of the this pointer
		i = 3; x = 2; l3();           // calls use(3, 2), as if i and x are both by reference

		auto l4 = [i, *this] { use(i, x); }; // makes a copy of *this, including a copy of x
		i = 4; x = 4; l4();					 // calls use(3, 2), as if i and x are both by copy
	}

private:
	int x{ 0 };

	void use(int i, int x) const { std::cout << "i = " << i << ", x = " << x << "\n"; }
};

struct MyObj {
	int value{ 123 };

	auto getValueCopy() {
		return [*this] { return value; }; // C++17
	}

	auto getValueRef() {
		return [this] { return value; }; // C++11
	}
};

class Data {
private:
	std::string name;

public:
	Data(const std::string& s) : name(s) { }

	auto startThreadWithCopyOfThis() const {
		// 开启并返回新线程,新线程将在3秒后使用this:
		std::thread t([*this] {
			using namespace std::literals;
			std::this_thread::sleep_for(3s);
			std::cout << "name: " << name << "\n";
		});

		return t;
	}
};

} // namespace

int test_lambda_17_this()
{
	//reference: https://en.cppreference.com/w/cpp/language/lambda
	S s;
	s.f();


	// reference: https://github.com/AnthonyCalandra/modern-cpp-features#lambda-capture-this-by-value
	MyObj mo;
	auto valueCopy = mo.getValueCopy();
	auto valueRef = mo.getValueRef();
	std::cout << "valueCopy: " << valueCopy() << ", valueRef: " << valueRef() << "\n"; // valueCopy: 123, valueRef: 123

	mo.value = 321;
	valueCopy();
	valueRef();
	std::cout << "valueCopy: " << valueCopy() << ", valueRef: " << valueRef() << "\n"; // valueCopy: 123, valueRef: 321


	std::thread t;
	{
		Data d{ "c1" };
		t = d.startThreadWithCopyOfThis();
	}   // d不再有效
	t.join();

	return 0;
}

      执行结果如下图所示:

C++17中lambda表达式新增加支持的features_第1张图片

      2.constexpr lambda:

      在C++17中lambda表达式可以声明为constexpr。constexpr关键字用于在编译时执行计算。
      自从C++17起,lambda表达式会尽可能的隐式声明constexpr。也就是说,任何只使用有效的编译期上下文(例如,只有字面量,没有静态变量,没有虚函数,没有try/catch,没有new/delete的上下文)的lambda都可以被用于编译期。
      使用编译期上下文中不允许的特性将会使lambda失去成为constexpr的能力,不过你仍然可以在运行时上下文中使用lambda.
      为了确定一个lambda是否能用于编译期,你可以将它声明为constexpr.
      当我们需要一个lambda表达式为constexpr时,我们最好显式的对lambda的表达式进行声明,当编译期不通过时,编译期会告诉我们哪里做错了。

      注意:
      (1).如果lambda表达式声明为constexpr,则需要遵循某些规则:如表达式的主体不应包含非constexpr的代码;
      (2).如果operator()满足constexpr函数的要求,或generic lambda特化为constexpr,则它始终是constexpr;
      (3).如果lambda说明符中使用了关键字constexpr,那么它也是constexpr;
      (4).如果lambda的结果满足constexpr函数的要求,则它是隐式constexpr;
      (5).如果lambda隐式或显式为constexpr,则转换为函数指针会生成constexpr函数;
      (6).如果我们使用编译期lambda初始化一个容器,那么编译器优化时很可能在编译期就计算出容器的初始值.
      (7).自从C++17起,如果lambda被显式或隐式地定义为constexpr,那么生成的函数调用运算符将自动是constexpr.

     测试代码如下:

namespace {

constexpr int addOne(int n) { return [n] { return n + 1; }(); } // reference: https://stackoverflow.com/questions/12662688/parentheses-at-the-end-of-a-c11-lambda-expression
constexpr auto addOne2(int n) { return [n] { return n + 1; }; } // 注:上下两条语句的区别
constexpr auto addOne3 = [](int n) { return n + 1; };

auto squared = [](auto val) { // 自从C++17起隐式constexpr
	constexpr int x{ 10 };
	return val * x;
};

// 为了确定一个lambda是否能用于编译期,你可以将它声明为constexpr
auto squared3 = [](auto val) constexpr { return val * val; }; // 自从C++17起
auto squared3i = [](int val) constexpr -> int { return val * val; };

// 自从C++17起,如果lambda被显式或隐式地定义为constexpr,那么生成的函数调用运算符将自动是constexpr
auto squared1 = [](auto val) constexpr { return val * val; }; // 编译期lambda调用
constexpr auto squared2 = [](auto val) { return val * val; }; // 编译期初始化squared2
constexpr auto squared4 = [](auto val) constexpr { return val * val; };

} // namespace

int test_lambda_17_constexpr()
{
	// 如果函数调用operator(或generic lambda的特化)为constexpr,则此函数为constexpr
	auto Fwd = [](int(*fp)(int), auto a) { return fp(a); };
	auto C = [](auto a) { return a; };
	static_assert(Fwd(C, 3) == 3);


	// reference: https://github.com/AnthonyCalandra/modern-cpp-features#constexpr-lambda
	auto identity = [](int n) constexpr { return n; };
	static_assert(identity(123) == 123);

	constexpr auto add = [](int x, int y) {
		auto L = [=] { return x; };
		auto R = [=] { return y; };
		return [=] { return L() + R(); };
	};
	static_assert(add(1, 2)() == 3);


	static_assert(addOne(1) == 2);
	static_assert(addOne2(1)() == 2);
	std::cout << "addOne:" << addOne(1) << ", addOne2: " << addOne2(1)() << "\n"; // addOne:2, addOne2: 2
	static_assert(addOne3(1) == 2);

	int v = [](int x, int y) { return x + y; }(5, 4);
	std::cout << "v: " << v << "\n"; // v: 9

	// 将一个lambda表达式嵌套在另一个lambda表达式中
	int v2 = [](int x) { return [](int y) { return y * 2; }(x)+3; }(5);
	std::cout << "v2: " << v2 << "\n"; // v2: 13


	// reference: https://learn.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-170
	// 当函数对象需要去修改通过副本传入的变量时,表达式必须用mutable修饰
	int m = 0, n = 0;
	[&, n](int a) mutable { m = ++n + a; }(4);
	std::cout << "m:" << m << ", n:" << n << "\n"; // m:5, n:0


	// 如果lambda的结果满足constexpr函数的要求,则它是隐式constexpr
	auto answer = [](int n) {
		return 32 + n;
	};

	constexpr int response = answer(10);
	static_assert(response == 42);

	// reference: https://github.com/MeouSker77/Cpp17/blob/master/markdown/src/ch06.md
	std::array arr;  // 自从C++17起 => std::array


	// 如果lambda隐式或显式为constexpr,则转换为函数指针会生成constexpr函数
	auto Increment = [](int n) {
		return n + 1;
	};

	constexpr int(*inc)(int) = Increment;

	return 0;
}

      执行结果如下图所示:

C++17中lambda表达式新增加支持的features_第2张图片

      lambda表达式的最短方式可以写为:[]{} :其没有参数,没有捕获任何东西,并且也不做实质性的执行。当函数对象需要去修改通过副本传入的变量时,表达式必须用mutable修饰。

      GitHub:https://github.com/fengbingchun/Messy_Test

你可能感兴趣的:(c++17)