推断类型,而浮动移动/副本无宏

Inferring types whilst eliding moves/copies without a macro

本文关键字:移动 副本 类型      更新时间:2023-10-16

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

代码在这里。

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

首先,标准从未保证过您追求的行为。ELISION是优化;任何实施都不需要。因此,没有一个人可以保证做您想做的事,尽管显然其中一些至少可以允许它。

转发有效地使ELINION不可能;关于这个事实,无能为力。完美的转发与参考和参考折叠有关;ELISION是关于初始化值参数的值,它在初始呼叫站点上不知道。

在现实情况下,这不是问题。实际上值得一试的大多数事情都是复制为昂贵的东西。复制几个int S或floats,尤其是对于琐碎的类别,甚至可能不会以剖面的效果出现。在绝大多数情况下,复制昂贵的对象之所以如此,是因为它们拥有某种资源,例如分配的内存。因此,大多数昂贵的类型的复制类型也可能是可移动的,因此会廉价移动。

无论如何,是的,如果您想具有责任的可能性,则不能使用转发。

在您的示例中,您仍然必须指定一次类型。如果您的目标是避免必须多次指定类型,则可以执行此操作:

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

请注意,如果您有更复杂的结构:

,这甚至有效
struct C {
  C(int,int) { }
  C(int) { }
};
auto z = Pair<C,C>{{1,2},3};

并且不需要副本。