【问题标题】:Inferring types whilst eliding moves/copies without a macro在没有宏的情况下省略移动/复制时推断类型
【发布时间】:2012-10-22 16:04:21
【问题描述】:

考虑以下简单的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

【问题讨论】:

  • I can make it happen for copies,不确定动作。 (注意:如果您传递左值,您的 make_pair 将创建一个 Pair&lt;T&amp;, U&amp;&gt;。;)因此,没有副本。)
  • 如果您的基于堆栈的类太大而成为问题,那您就错了。
  • @Xeo:呵呵,我想知道这是故意的还是我应该说什么。一点点 remove_referencedecay 很可能在那个 maker 函数中是有序的。
  • @KerrekSB,Xeo:这不是特别设计的,但这不是主要问题。我遇到的主要问题是临时工应该就地建造。它们在示例 (1) 和 (3) 中,但不在 (2) 中。
  • 当要求我的编译器进行优化时,虽然特殊成员的输出仍在执行,但对CPairmake_pair 的所有引用都将被删除。可以肯定的是,从保留其副作用的意义上说,副本并没有被忽略。但无论如何,这些对象都不在这里。考虑到副作用可能是为了仪表而放在这里的,所以我真的不知道问题是什么。

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


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

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

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

    struct C {
      C(int,int) { }
      C(int) { }
    };
    
    auto z = Pair<C,C>{{1,2},3};
    

    并且不需要副本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-26
      • 2015-09-30
      • 1970-01-01
      • 2019-05-06
      • 1970-01-01
      • 1970-01-01
      • 2012-01-16
      • 1970-01-01
      相关资源
      最近更新 更多