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

Cli*_*ton 6 c++ templates constructor c++11

考虑以下简单make_pair类:

template <class X, class Y>
struct Pair
{
    X x;
    Y y;
};
Run Code Online (Sandbox Code Playgroud)

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

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;
};
Run Code Online (Sandbox Code Playgroud)

然后我们可以运行:

auto z1 = Pair<C, C>{C(1),C(2)};
Run Code Online (Sandbox Code Playgroud)

没有输出,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)};
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以做到:

auto z2 = make_pair(C(3),C(4));
Run Code Online (Sandbox Code Playgroud)

但这打印:

Move: 3
Move: 4
Run Code Online (Sandbox Code Playgroud)

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

但是接下来我们定义这个宏:

#define MAKE_PAIR(x,y) decltype(make_pair(x,y)){x,y}
Run Code Online (Sandbox Code Playgroud)

然后我们可以这样做:

auto z3 = MAKE_PAIR(C(5),C(6));
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

代码在这里.

Nic*_*las 1

我无法想象 C++ 会强迫人们使用宏来获得我想要的行为,因为显然 C++ 正在远离宏。

标准一开始就无法保证您所追求的行为。省略是一种优化;任何实施都不需要它。因此,它们都不能保证做你想做的事,尽管显然其中一些至少允许它成为可能。

有效转发使省略成为不可能;对于这个事实我们无能为力。完美转发就是引用和引用折叠;省略是关于值初始化值参数,它在初始调用站点无法知道。

在现实世界中,这不应该是一个问题。大多数真正值得忽略的东西都是复制成本高昂的东西。复制几个ints 或floats,特别是对于一个简单的类,甚至可能不会在分析器上显示为一个点。在绝大多数情况下,复制成本高昂的对象之所以如此,是因为它们拥有某种资源,例如分配的内存。因此,大多数复制成本高昂的类型也可以移动,因此移动成本较低。

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