在没有宏的情况下消除移动/复制的类型

时间:2012-11-02 00:50:25

标签: c++ templates constructor c++11

考虑以下简单的make_pair类:

template <class X, class Y>
struct Pair
{
    X x;
    Y y;
};

另外,我们将制作一个简单的类来显示任何移动/副本:

struct C
{
    C(int n_) : n(n_) {};
    C(const C& x) { n = x.n; std::cout << "Copy: " << n << std::endl; }
    C(C&& x)      { n = x.n; std::cout << "Move: " << n << std::endl; }
    int n;
};

然后我们可以运行:

auto z1 = Pair<C, C>{C(1),C(2)};

没有输出,C未被移动或复制。

但是,我们必须在构造函数Pair中指定类型。让我们说我们想推断这些。我们可以这样做:

template <class X, class Y>
Pair<X, Y> make_pair(X&& x, Y&& y)
{
    return Pair<X, Y>{std::forward<X>(x), std::forward<Y>(y)};
}

然后我们可以做到:

auto z2 = make_pair(C(3),C(4));

但这会打印出来:

Move: 3
Move: 4

如果C是堆分配类型,则不成问题,但如果堆栈分配类型,则移动基本上是副本。

然后让我们定义这个宏:

#define MAKE_PAIR(x,y) decltype(make_pair(x,y)){x,y}

然后我们可以这样做:

auto z3 = MAKE_PAIR(C(5),C(6));

这样做类型扣除并且不需要移动。但是我们需要创建一个宏,我觉得它有点混乱,也阻止我们使用运算符来做这类事情。

是否有解决方案可以执行以下操作:

(1)演绎类型(如2和3)
(2)不需要复制或移动(如1和3)
(3)不需要宏(如1和2)

我能得到的最好的是三分之二,但肯定有三分之三可能吗?我无法想象C ++会强迫人们使用宏来获得我所追求的行为,因为显然C ++正在远离宏。

代码为here

2 个答案:

答案 0 :(得分:1)

  

我无法想象C ++会强迫一个人使用宏来获取我所追求的行为,因为显然C ++正在远离宏。

你所追求的行为从来没有得到标准的保证。 Elision是优化;任何实施都不需要它。所以他们都不能保证做你想做的事,虽然显然有些人至少可以做到这一点。

有效地转发使得无法进行省略;对于这个事实,没有什么可以做的。完美的转发是关于参考和参考折叠; elision是关于初始化值参数的值,它在初始调用站点时无法知道。

在实际情况中,这应该不是问题。实际上值得忽略的大多数事情都是复制费用昂贵的事情。复制一些intfloats,特别是对于一个简单的类,可能甚至不会在分析器上显示为blip。在绝大多数情况下,复制昂贵的对象是因为它们拥有某种资源,例如分配的内存。因此,大多数复制成本昂贵的类型也可以移动,因此可以便宜地移动。

无论如何,是的,如果你想有可能获得省略,你就不能使用转发。

答案 1 :(得分:0)

在您的示例中,您仍然需要指定一次类型。如果您的目标是避免不止一次指定类型,则可以执行以下操作:

auto z = Pair<C,C>{3,4};

请注意,如果您有更复杂的构造函数,这甚至可以工作:

struct C {
  C(int,int) { }
  C(int) { }
};

auto z = Pair<C,C>{{1,2},3};

并且不需要副本。