【问题标题】:C++11 use-case for piecewise_construct of pair and tuple?对和元组的分段构造的 C++11 用例?
【发布时间】:2011-09-03 23:13:53
【问题描述】:

N3059 中,我找到了对(和元组)分段构造的描述(并且在新标准中)。

但我不知道什么时候应该使用它。我发现了关于 emplace 和不可复制实体的讨论,但是当我尝试它时,我无法创建一个我 需要 piecewiese_construct 的案例或可以看到性能优势.

示例。我认为我需要一个不可复制的类,但是movebale(转发所需):

struct NoCopy {
  NoCopy(int, int) {};
  NoCopy(const NoCopy&) = delete; // no copy
  NoCopy& operator=(const NoCopy&) = delete; // no assign
  NoCopy(NoCopy&&) {}; // please move
  NoCopy& operator=(NoCopy&&) {}; // please move-assign
};

然后我有点预计标准对构造会失败:

pair<NoCopy,NoCopy> x{ NoCopy{1,2}, NoCopy{2,3} }; // fine!

但它没有。实际上,这正是我所期望的,因为“移动东西”而不是在 stdlib 中的任何地方复制它,应该是这样吗?

因此,我认为我没有理由这样做,或者这样:

pair<NoCopy,NoCopy> y(
    piecewise_construct,
    forward_as_tuple(1,2),
    forward_as_tuple(2,3)
); // also fine
  • 那么,用例是什么?
  • 如何以及何时使用piecewise_construct

【问题讨论】:

  • 尝试同时禁用移动构造函数。

标签: c++11 tuples use-case forwarding piecewise


【解决方案1】:

并非所有类型都可以比复制更有效地移动,对于某些类型,甚至明确禁用复制和移动可能是有意义的。将std::array&lt;int, BIGNUM&gt; 视为前一种类型的示例。

emplace 函数和piecewise_construct 的要点是可以就地构造这样的类,而无需创建要移动或复制的临时实例。

struct big {
    int data[100];
    big(int first, int second) : data{first, second} {
        // the rest of the array is presumably filled somehow as well
    }
};

std::pair<big, big> pair(piecewise_construct, {1,2}, {3,4});

将上面的内容与 pair(big(1,2), big(3,4)) 进行比较,其中必须创建两个临时的 big 对象,然后复制 - 移动在这里根本没有帮助!同样:

std::vector<big> vec;
vec.emplace_back(1,2);

分段构造 pair 的主要用例是将元素放入 mapunordered_map

std::map<int, big> map;
map.emplace(std::piecewise_construct, /*key*/1, /*value*/{2,3});

【讨论】:

  • 你描述得很好,谢谢。这也是我的想法。但如果您禁用在目标类上移动(使用=delete),pair(piecewise_construct,...) 将不起作用。我用 gcc-4.7.0 试了一下,得到一个编译错误。原因:pair 的实现:
    template&lt;class... _Args1, class... _Args2&gt; pair(piecewise_construct_t, tuple&lt;_Args1...&gt; __first, tuple&lt;_Args2...&gt; __second) : first(__cons&lt;first_type&gt;(std::move(__first))), second(__cons&lt;second_type&gt;(std::move(__second))) { } 如您所见,需要移动emplace 好像没有用。
  • 好的,gcc-4.7.0 似乎还没有提供map.emplace()。但这仍然让我对pair(piecewise_construct, {...},{...}) 产生疑问。这是否适用于 non-copyable non-movable 对象?从 Std 20.3.2.(14) 中的表述来看,似乎是这样。虽然“转发”是mehtioned,但它似乎适用于构造函数的参数,而不是对象本身(那么可能需要移动?)。
  • @towi :我认为 gcc-4.7.0+libstdc++ 对于具有分段构造的对构造函数尚不兼容。不可复制和不可移动的对象应该可以工作,例如 clang+libc++ 会给出正确的结果。 @JohannesD:我不明白您为什么认为 map.emplace 应该与 piecewise_construct 一起使用?我知道的每个提议似乎都暗示 emplace 的用法应该是map.emplace(Key, ArgForValue...),例如map.emplace(/*key*/1, /*value*/ 2, 3);(这就是clang+libc++ 的行为方式)
  • map.emplace(std::piecewise_construct, /*key*/1, /*value*/{2,3}); 不会编译,而是应该使用map.emplace(std::piecewise_construct, std::forward_as_tuple(1), std::forward_as_tuple(2,3));,因为std::pair 的适当构造函数是template&lt; class... Args1, class... Args2 &gt; pair(std::piecewise_construct_t, std::tuple&lt;Args1...&gt; first_args, std::tuple&lt;Args2...&gt; second_args );
  • std::pair&lt;big, big&gt; pair(piecewise_construct, {1,2}, {3,4}); 也不编译
【解决方案2】:

piecewise_construct 的一个功能是在进行重载解析以构造对象时避免错误的转换。

考虑一个有一组奇怪的构造函数重载的Foo

struct Foo {
    Foo(std::tuple<float, float>) { /* ... */ }
    Foo(int, double) { /* ... */ }
};

int main() {
    std::map<std::string, Foo> m1;
    std::pair<int, double> p1{1, 3.14};

    m1.emplace("Will call Foo(std::tuple<float, float>)",
               p1);

    m1.emplace("Will still call Foo(std::tuple<float, float>)",
               std::forward_as_tuple(2, 3.14));

    m1.emplace(std::piecewise_construct,
               std::forward_as_tuple("Will call Foo(int, double)"),
               std::forward_as_tuple(3, 3.14));

    // Some care is required, though...
    m1.emplace(std::piecewise_construct,
               std::forward_as_tuple("Will call Foo(std::tuple<float, float>)!"),
               std::forward_as_tuple(p1));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    • 2021-09-13
    • 2012-08-04
    • 2012-05-02
    • 1970-01-01
    相关资源
    最近更新 更多