C++ 移动省略优化

C++ 移动省略优化,c++,c++11,optimization,move-semantics,perfect-forwarding,C++,C++11,Optimization,Move Semantics,Perfect Forwarding,考虑一个类的两种实现: struct S1 { std::vector< T > v; void push(T && x) { v.push_back(std::move(x)); } void push(T const & x) { push(T(x)); } void pop() { v.pop_back(); } void replace(T && x) { pop(); push(std::mov

考虑一个类的两种实现:

struct S1
{
    std::vector< T > v;
    void push(T && x) { v.push_back(std::move(x)); }
    void push(T const & x) { push(T(x)); }
    void pop() { v.pop_back(); }
    void replace(T && x) { pop(); push(std::move(x)); }
    void replace(T const & x) { replace(T(x)); }
};

struct S2
{
    std::vector< T > v;
    void push(T x) { v.push_back(std::move(x)); }
    void pop() { v.pop_back(); }
    void replace(T x) { pop(); push(std::move(x)); }
};
struct S1
{
std::vectorv;
空推(T&&x){v.push_-back(std::move(x));}
无效推力(T常数&x){push(T(x));}
void pop(){v.pop_back();}
void replace(T&&x){pop();push(std::move(x));}
void replace(T常数&x){replace(T(x));}
};
结构S2
{
std::vectorv;
空推(tx){v.push_-back(std::move(x));}
void pop(){v.pop_back();}
void replace(tx){pop();push(std::move(x));}
};
S1
push
重载正好表达了我想要的内容
S2
push
是一种以不太冗长的方式表达它的方法

但我担心对象的过度移动构造有一个缺点


现代编译器能否将
std::move(T(std::move(T))
表达式简化为
std::move(T)
对于某些
T
,其中
decltype(T)
T&
?现代编译器能优化不必要的移动吗?或者这是标准所禁止的吗?

不,省略是不合法的,除非是在“仿佛”优化下

现在,如果
foo()

但是如果我们
S{}.push(std::move(foo())
,显式
std::move
会阻止省略的可能性

通常更好的方法是基于部署的作战,而不是基于推送的作战

template<class...Args>
void emplace(Args&&...args) {
  v.emplace_back( std::forward<Args>(args)... );
}

如果您希望获得SFINAE支持。如果不明显的话,一条评论说“我们希望在这里构建一个
T
”,也是礼貌的。

我在提供的代码中没有看到任何
std::move(T(std::move(T))
。我也不理解
push(T(x))的含义;
在S.@SergeyA的push for first flavor的第二个版本中,如果您试图了解一些
T的情况;
作为
S2 S2;S2.push(std::move(T))
,那么您可以看到
std::move(T)(std::move(T)))
。第二种方法只是减少代码重复的一种方法。另请参见。它不会直接回答您字面上要求的内容,而是您可能想要的内容。
std::forward
是件好事,但在这种情况下,我应该添加信息性/防御性
std::enable_,如果:value>
/
std::在函数体中启用\U if\U t,t>
SFINAE-guard或
static\U-assert
离子,让这个temlate函数的用户知道应该传递给它的(类型)类型。最后它变得太冗长了。@Orient I Take
。你可以(一起)传递任何可以传递的内容构造一个
T
,而不仅仅是一个参数。is_same将不起作用。这样做的缺点是
{}
构造不起作用,而是删除
{}
将主要解决这一问题。是的,如果需要,您可以添加SFINAE样板文件。或者,比如说,添加注释。模板成员函数实现通常不能从标题隐藏。@Orient True?现在,如果您想隐藏实现,但又拥有
emplace
的全部功能,您可以编写并使用
构造
类型擦除helper,它获取任何可以构造
T
,存储对它的引用,并允许您在选择的位置从它构造一次
T
。遗憾的是,向量不支持传递“绑定构造函数”对于push_-back,您必须使用原始备份存储,或者教
construct
与一些(有限的)容器交互以进行安放。但这很愚蠢。
template<class...Args,
  decltype(T(std::declval<Args&&>()...))* =0
>
void emplace(Args&&...args) {
  v.emplace_back( std::forward<Args>(args)... );
}