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