How to allow copy elision construction for C++ classes (not just POD C structs)

Consider the following code:

#include 
#include 

struct A
{
  A() {}
  A(const A&) { std::cout << "Copy" << std::endl; }
  A(A&&) { std::cout << "Move" << std::endl; }
};

template 
struct B
{
  T x;
};

#define MAKE_B(x) B{ x }

template 
B make_b(T&& x)
{
  return B { std::forward(x) };
}

int main()
{
  std::cout << "Macro make b" << std::endl;
  auto b1 = MAKE_B( A() );
  std::cout << "Non-macro make b" << std::endl;
  auto b2 = make_b( A() );
}

This outputs the following:

Macro make b
Non-macro make b
Перемещение

Обратите внимание, что b1 строится без перемещения, но построение b2 требует перемещения.

Мне также нужно ввести вывод, поскольку A в реальной жизни может быть сложным типом, который сложно написать явно. Мне также нужно иметь возможность вкладывать вызовы (например, make_c (make_b (A ())) ).

Возможна ли такая функция?

Дополнительные мысли:

N3290 Final C + + 0x черновик страницы 284:

Это исключение операций копирования / перемещения, называется копированием, разрешено в следующие обстоятельства:

когда временный объект класса , имеющий не привязан к ссылке (12.2) будет скопирован / перемещен в класс объект с таким же CV-неквалифицированным типа, операция копирования / перемещения может быть опущено путем создания временного объект прямо в цель omitted copy/move

Unfortunately this seems that we can't elide copies (and moves) of function parameters to function results (including constructors) as those temporaries are either bound to a reference (when passed by reference) or no longer temporaries (when passed by value). It seems the only way to elide all copies when creating a composite object is to create it as an aggregate. However, aggregates have certain restrictions, such as requiring all members be public, and no user defined constructors.

I don't think it makes sense for C++ to allow optimizations for POD C-structs aggregate construction but not allow the same optimizations for non-POD C++ class construction.

Is there any way to allow copy/move elision for non-aggregate construction?

My answer:

This construct allows for copies to be elided for non-POD types. I got this idea from David Rodríguez's answer below. It requires C++11 lambdas. In this example below I've changed make_b to take two arguments to make things less trivial. There are no calls to any move or copy constructors.

#include 
#include 

struct A
{
  A() {}
  A(const A&) { std::cout << "Copy" << std::endl; }
  A(A&&) { std::cout << "Move" << std::endl; }
};

template 
class B
{
public:
  template 
  B(const LAMBDA1& f1, const LAMBDA2& f2) : x1(f1()), x2(f2()) 
  { 
    std::cout 
    << "I'm a non-trivial, therefore not a POD.\n" 
    << "I also have private data members, so definitely not a POD!\n";
  }
private:
  T x1;
  T x2;
};

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

#define MAKE_B(x1, x2) make_b(DELAY(x1), DELAY(x2))

template 
auto make_b(const LAMBDA1& f1, const LAMBDA2& f2) -> B
{
  return B( f1, f2 );
}

int main()
{
  auto b1 = MAKE_B( A(), A() );
}

If anyone knows how to achieve this more neatly I'd be quite interested to see it.

Previous discussion:

This somewhat follows on from the answers to the following questions:

Can creation of composite objects from temporaries be optimised away?
Avoiding need for #define with expression templates
Eliminating unnecessary copies when building composite objects

28
задан Community 23 May 2017 в 12:31
поделиться