有没有办法实现移动消除



运行时:

struct Copy
{
Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
Copy(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
~Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
};
char buffer[1024];
template <typename Type>
Type * push(Type value)
{
return new(buffer) Type(std::move(value));
};
int main()
{
push(Copy());
return 0;
}

我得到以下输出:

Copy::Copy()
Copy::Copy(Copy &&)
Copy::~Copy()

有没有办法消除移动构造函数?

我希望使用 -O3 可以就地构建它,但根据我的测试,情况似乎并非如此。

有没有办法省略移动构造函数?[...]我希望使用 -O3,它会就地构建,

好吧,您正在显式调用移动构造函数...并且对象正在就地构建buffer.为什么希望省略移动构造函数调用?

template <typename Type>
Type * push(Type value)
{ 
//                 invokes `Type::Type(Type&&)`
//                 vvvvvvvvvvvvvvvvvvvvvv
return new(buffer) Type(std::move(value));
//                      ^^^^^^^^^^^^^^^^
//                      casts `value` to `Type&&`
};

如果您尝试使用其他类型的值构造Copy,您的问题会更有意义。 例如:

struct Copy
{
Copy(int) { std::cout << "INTn"; }
// ... other ctors ...
};
template <typename Type, typename Arg>
Type * push(Arg x)
{
return new(buffer) Type(std::move(x));
};
int main()
{
push<Copy>(1);
}

上面的代码打印:

国际

不调用移动/复制构造函数。

魔杖盒上的现场示例

我认为你不能这样做。 因为 elision 要求编译器对构造对象的位置有内在的知识。 有了这些信息,它就可以避免移动和复制,只需将对象放置在需要去的地方。 例如,当您从一个函数的堆栈中返回某些内容返回给另一个函数时,编译器可以省略移动/复制。

但是在您的情况下,放置 new 允许您将对象构造到任意缓冲区中。 该缓冲区实际上可以在任何地方,例如它可以在堆栈上(就像在您的情况下一样),也可以使用new在堆上分配。 因此,编译器不会在此处省略移动/复制。

严格来说,这可以通过对代码进行一些分析来实现,因为编译器已经知道缓冲区在哪里,但我怀疑大多数编译器都实现了这一点。


请注意,您已经声明了一个字符指针数组而不是字符数组,因此如果这是您所期望的,缓冲区的长度超过 1024 字节


注意另请注意,显式调用std::move可以防止省略


在这种情况下,您能做的最好的事情是创建一个就地构造函数或移动构造函数(如上所述)以将该对象构造到缓冲区中。 就地构造函数看起来像这样

template <typename... args>
void construct(std::in_place_t, Args&&... args) {
new (buffer) Type{std::forward<Args>(args)...};
}

使用具有完美参数转发的 emplace 函数。既然您即将开始新安置的冒险,还有几件事要说:

  1. 使用std::aligned_storage_t<>进行存储。它保证您的对象将正确对齐。

  2. 读取并使用放置new的返回值。在简单的情况下,它不会与您提供的地址不同。然而,标准允许它,并且在复杂的类层次结构中可能是这样。

更新的示例:

#include <iostream>
struct Copy
{
Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(const Copy & other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
Copy(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
Copy & operator=(Copy && other) {std::cerr << __PRETTY_FUNCTION__ << std::endl; return *this;}
~Copy() {std::cerr << __PRETTY_FUNCTION__ << std::endl;}
};
std::aligned_storage_t<sizeof(Copy), alignof(Copy)> buffer[4];
template <typename Type, typename LegalStorage, typename...Args>
auto emplace(LegalStorage* buffer, Args&&...args) -> Type*
{
return new(buffer) Type(std::forward<Args>(args)...);
};
int main()
{
auto p1 = emplace<Copy>(buffer /* constructor arguments go here*/);
auto p2 = emplace<Copy>(&buffer[1]/* constructor arguments go here*/);
auto p3 = emplace<Copy>(buffer + 2/* constructor arguments go here*/);
auto p4 = emplace<Copy>(buffer + 3/* constructor arguments go here*/);
return 0;
}

最新更新