如何在C ++ 0x中传递lambda?

时间:2011-07-14 09:50:21

标签: c++ lambda c++11

似乎在C ++ 0x中构造对象的方法是避免复制/移动(特别是对于大堆栈分配的对象)是“通过lambda传递”。

请参阅以下code

#include <iostream>

#define LAMBDA(x) [&] { return x; }

class A
{
public:
  A() {};
  A(const A&) { std::cout << "Copy "; }
  A(A&&) { std::cout << "Move "; }
};

class B1
{
public:
  B1(const A& a_) : a(a_) {}
  B1(A&& a_) : a(std::move(a_)) {}
  A a;
};

class B2
{
public:
  B2(const A& a_) : a(a_) {}
  B2(A&& a_) : a(std::move(a_)) {}
  template <class LAMBDA_T>
  B2(LAMBDA_T&& f, decltype(f())* dummy = 0) : a(f()) {}
  A a;
};

int main()
{
  A a;
  std::cout << "B1 b11(       a ): ";
  B1 b11(a);
  std::cout << std::endl;
  std::cout << "B2 b12(LAMBDA(a)): ";
  B2 b12(LAMBDA(a));
  std::cout << std::endl;
  std::cout << std::endl;

  std::cout << "B1 b21(       std::move(a) ): ";
  B1 b21(std::move(a));
  std::cout << std::endl;
  std::cout << "B2 b22(LAMBDA(std::move(a))): ";
  B2 b22(LAMBDA(std::move(a)));
  std::cout << std::endl;
  std::cout << std::endl;

  std::cout << "B1 b31((       A() )): "; 
  B1 b31((A())); 
  std::cout << std::endl;
  std::cout << "B2 b32((LAMBDA(A()))): ";
  B2 b32((LAMBDA(A()))); 
  std::cout << std::endl;
  std::cout << std::endl;
}

其中输出以下内容:

B1 b11(       a ): Copy 
B2 b12(LAMBDA(a)): Copy 

B1 b21(       std::move(a) ): Move 
B2 b22(LAMBDA(std::move(a))): Move 

B1 b31((       A() )): Move 
B2 b32((LAMBDA(A()))): 

注意“传递lambda”会移除参数是我认为称为“prvalue”的情况下的移动。

请注意,似乎“传递lambda”方法仅在参数为“prvalue”时才有用,但在其他情况下似乎没有受到伤害。

有没有让函数接受C ++ 0x中的“传递lambda”参数,这比客户端必须在lambda函数中包装它们的参数更好? (除了定义调用该函数的代理宏之外)。

2 个答案:

答案 0 :(得分:3)

如果您对模板化构造函数没有问题,那么您最好使用完美转发而不是使用lambdas进行模糊处理。

class super_expensive_type {
public:
    struct token_t {} static constexpr token = token_t {};

    super_expensive_type(token_t);
}
constexpr super_expensive_type::token_t super_expensive_type::token;

class user {
public:
    template<typename... Args>
    explicit
    user(Args&&... args)
        : member { std::forward<Args>(args)... }
    {}

private:
    super_expensive_type member;
};

// ...

// only one construction here
user { super_expensive_type::token };

super_expensive_type moved_from = ...;
// one move
user { std::move(moved_from) };

super_expensive_type copied_from = ...;
// one copy
user { copied_from };

使用lambdas不能比这更好,因为必须返回lambda体中表达式的结果。

答案 1 :(得分:2)

你正在做的事情存在根本问题。你不能魔法一个物体存在。变量必须是:

  1. 默认构造
  2. 复制构造
  3. 移动构造
  4. 使用不同的构造函数构建。
  5. 由于你只定义了前三个,所以

    4不在桌面上。你的副本和移动构造函数都打印东西。因此,可以得出的唯一结论是,如果没有打印任何内容,则该对象是默认构造的。 IE:填充没有

    简而言之,基于Lambda的传输机制似乎根本没有转移任何东西。


    经过进一步分析,我看到发生了什么。你的lambda实际上并没有通过引用获取值;它是构建一个值。如果你展开宏,你得到的是:

    B2 b32(([&] {return A()}));
    

    构建一个临时的;它实际上没有任何参考。所以我不确定你怎么能认为这会“传递”任何东西。你所做的只是制作一个构造一个对象的函数。您可以轻松地将B2::a的构造函数的参数传递给B2的构造函数,并让它使用它们来创建对象,它会给您带来相同的效果。

    你没有传递一个值。您正在创建一个始终创建完全相同对象的函数。这不是很有用。