C++ 在构造类成员时消除副本

C++ 在构造类成员时消除副本,c++,c++11,c++14,C++,C++11,C++14,对于两个类型为T和U的任意对象,它们组成一个类,如下所示 template <class T, class U> struct Comp { T t_m; U u_m; }; 但是我不知道他们的移动构造器的行为如何,或者他们是否有任何行为 由于我的类似乎可以是一个聚合,我想知道删除构造函数并允许是否是一个更好的解决方案,即编写如下代码: Comp{ get_temporary_T(), get_temporary_U() }; 或者如果使用它有好处的话 附

对于两个类型为
T
U
的任意对象,它们组成一个类,如下所示

template <class T, class U>
struct Comp
{
    T t_m; 
    U u_m; 
}; 
但是我不知道他们的移动构造器的行为如何,或者他们是否有任何行为

由于我的类似乎可以是一个聚合,我想知道删除构造函数并允许是否是一个更好的解决方案,即编写如下代码:

Comp{ get_temporary_T(), get_temporary_U() }; 
或者如果使用它有好处的话

附言 就地构造(使用placement new操作符)不是我正在寻找的解决方案

PS 2 我想象
std::tuple
使用了这样一种最佳方法,因为通过调用tuple构造函数可以利用临时变量

auto t = std::make_tuple(10, "Test", 3.14, std::ref(n), n);

或者,有人可以详细说明这是如何实现的吗?

您对移动构造函数的建议似乎是最好的方法

处理临时对象时,最好(也是最优化的)是移动成员中的参数。但是正如你所说的,移动构造函数可能不存在

如果只有一个参数,那么很容易检查它是否是move-constructible,并移动它,然后复制它。您可以使用
std::enable\u if
std::is\u move\u constructible

但如果参数不止一个,则必须检查所有组合。例如,对于2个参数,您必须有4个构造函数:复制/复制、移动/复制、复制/移动和移动/移动。因此,它不是真正可伸缩的,因此更适合复制参数


使用聚合初始化时,参数为复制不移动。

这已经是最佳的:

Comp(T&& t, U&& u)
: t_m(std::move(t))
, u_m(std::move(u))
{ }
如果
T
有一个移动构造函数,这将是一个移动。如果没有,这将是一个副本-但没有办法在某处复制。而且它是不可复制的,那么整个问题就没有意义了

当然,这只适用于右值,所以您也需要左值。不幸的是,这有点复杂:

template <class Tx, class Ux, 
    class = std::enable_if_t<std::is_convertible<std::decay_t<Tx>*, T*>::value &&
                             std::is_convertible<std::decay_t<Ux>*, U*>::value>>
Comp(Tx&& t, Ux&& u)
: t_m(std::forward<Tx>(t))
, u_m(std::forward<Ux>(u))
{ }

有了这一点,我们就可以通过就地构建来避免任何形式的复制或移动。这是否有益取决于您对
Comp
的使用

太宽了?“用(可用的)临时性构造它们的最佳方法(在最小化复制操作方面)是什么?”太宽泛了?为什么不先测试最简单的代码,然后再开始尝试超越编译器的优化器呢?@PaulMcKenzie你能帮我做些什么吗?你的意思是检查机器代码、速度基准、测试我的代码吗?“我想std::tuple使用了这样一种最佳方法,因为make_tuple通过调用tuple构造函数来利用临时变量:“这里不清楚你指的是什么,或者说,
make_tuple
的功能在任何方面都比第一个选项更为优化。顺便说一下,您可以检查您的类型是否有移动构造函数:太好了!另一方面,
std::tuple
现在支持分段构造吗?(我以前没有听说过这个,在搜索时我发现了)
template <class Tx, class Ux, 
    class = std::enable_if_t<std::is_convertible<std::decay_t<Tx>*, T*>::value &&
                             std::is_convertible<std::decay_t<Ux>*, U*>::value>>
Comp(Tx&& t, Ux&& u)
: t_m(std::forward<Tx>(t))
, u_m(std::forward<Ux>(u))
{ }
template <class... TArgs, class... UArgs,
    class = std::enable_if_t<std::is_constructible<T, TArgs...>::value &&
                             std::is_constructible<U, UArgs...>::value>>
Comp(std::piecewise_construct_t pc, std::tuple<TArgs...> const& t, std::tuple<UArgs...> const& u)
: Comp(t, u, std::index_sequence_for<TArgs...>{}, std::index_sequence_for<UArgs...>{})
{ }

private:
template <class TTuple, class UTuple, size_t... Is, size_t... Js>
Comp(TTuple const& t, UTuple const& u, std::index_sequence<Is...>, std::index_sequence<Js...> )
: t_m(std::get<Is>(t)...)
, u_m(std::get<Js>(u)...)
{ }