std::forward_as__tuple 的用例

Use case for std::forward_as__tuple

Cppreference 为 std::forward_as_tuple 提供以下示例(参见 here

#include <iostream>
#include <map>
#include <tuple>
#include <string>

int main()
{
     std::map<int, std::string> m;

     m.emplace(std::piecewise_construct,
               std::forward_as_tuple(10),
               std::forward_as_tuple(20, 'a'));
     std::cout << "m[10] = " << m[10] << '\n';

     // The following is an error: it produces a
     // std::tuple<int&&, char&&> holding two dangling references.
     //
     // auto t = std::forward_as_tuple(20, 'a');
     // m.emplace(std::piecewise_construct, std::forward_as_tuple(10), t);
}

与简单的写作相比有什么好处

 m.emplace(std::make_pair(20,std::string(20,'a')));

它避免制作不必要的或可能不可能的对象副本。

首先,让我们考虑 std::string 以外的值类型。我将使用无法复制的东西,但这也适用于 可以 复制但这样做很昂贵的东西:

struct NonCopyable
{
    NonCopyable(int a, char b) {} // Dummy
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};

我们如何将其插入 std::map<int, NonCopyable> m?让我们来看看可能性:

m.insert({10, NonCopyable{10, 'a'}});

这个won't work。它接受对 std::pair 的引用并复制它,这需要复制 NonCopyable 对象,这是不可能的。

m.emplace(10, NonCopyable{10, 'a'}});

这也是won't work。虽然它就地构造 std::pair 值,但它仍然必须复制 NonCopyable 对象。

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::tuple{10, 'a'});

最后,worksstd::pair 元素与其两个子对象一样就地构建。

但是让我们考虑另一种情况。考虑这个 class:

struct UsesNonCopyable
{
    UsesNonCopyable(const NonCopyable&) {}
    UsesNonCopyable(const UsesNonCopyable&) = delete;
    UsesNonCopyable& operator=(const UsesNonCopyable&) = delete;
};

现在我们如何向 std::map<int, UsesNonCopyable> m 添加元素?

上面的前两个选项将不起作用,原因与它们在前一个案例中不起作用的原因相同,但突然间第三个选项也不起作用:

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::tuple{NonCopyable{10, 'a'}});

这个won't work因为NonCopyable对象必须复制到传递给std::pairstd::tuple对象' s 构造函数。

这是 std::forward_as_tuple 的用武之地:

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::forward_as_tuple(NonCopyable{10, 'a'}));

This works,因为现在不是传递 m.emplace 包含 NonCopyable 对象的 copy 的元组,而是使用 std::forward_as_tuple 构造一个元组,其中包含 NonCopyable 对象的引用。该引用被转发到 std::pair 的构造函数,后者又将其转发到 UsesNonCopyable 的构造函数。


请注意,使用 C++17 添加的 std::map::try_emplace as long as your key type is copyable. The following will work 可以很好地消除这种复杂性,而且要简单得多:

std::map<int, UsesNonCopyable> m;
m.try_emplace(10, NonCopyable{10, 'a'});