6

考虑以下简单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++ 正在远离宏。

代码在这里

4

2 回答 2

1

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

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

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

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

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

于 2012-11-02T05:59:45.383 回答
0

在您的示例中,您仍然必须指定一次类型。如果您的目标是避免多次指定类型,您可以这样做:

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

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

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

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

并且不需要副本。

于 2012-11-02T06:00:09.910 回答