std::forward_as__tuple 的用例

Jan*_*erg 4 c++ stdmap std c++11

Cppreference 给出std::forward_as_tuple了以下示例(请参见此处

#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);
}
Run Code Online (Sandbox Code Playgroud)

简单写有什么好处

 m.emplace(std::make_pair(20,std::string(20,'a')));
Run Code Online (Sandbox Code Playgroud)

Mil*_*nek 5

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

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

struct NonCopyable
{
    NonCopyable(int a, char b) {} // Dummy
    NonCopyable(const NonCopyable&) = delete;
    NonCopyable& operator=(const NonCopyable&) = delete;
};
Run Code Online (Sandbox Code Playgroud)

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

m.insert({10, NonCopyable{10, 'a'}});
Run Code Online (Sandbox Code Playgroud)

行不通。它接受对 a 的引用std::pair并复制它,这需要复制NonCopyable对象,这是不可能的。

m.emplace(10, NonCopyable{10, 'a'}});
Run Code Online (Sandbox Code Playgroud)

这也行不通。当它就地构造std::pair值时,它仍然必须复制NonCopyable对象。

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::tuple{10, 'a'});
Run Code Online (Sandbox Code Playgroud)

最后,一些作品。该std::pair元素是就地构造的,它的两个子对象也是如此。

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

struct UsesNonCopyable
{
    UsesNonCopyable(const NonCopyable&) {}
    UsesNonCopyable(const UsesNonCopyable&) = delete;
    UsesNonCopyable& operator=(const UsesNonCopyable&) = delete;
};
Run Code Online (Sandbox Code Playgroud)

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

由于与前一种情况相同的原因,上面的前两个选项不起作用,但突然之间第三个选项也不起作用:

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::tuple{NonCopyable{10, 'a'}});
Run Code Online (Sandbox Code Playgroud)

将不起作用,因为必须将NonCopyable对象复制std::tuple传递给std::pair的构造函数的对象中。

这是std::forward_as_tuple进来的地方:

m.emplace(std::piecewise_construct,
          std::tuple{10},
          std::forward_as_tuple(NonCopyable{10, 'a'}));
Run Code Online (Sandbox Code Playgroud)

这个作品,因为现在而不是传递m.emplace包含一个元组副本中的NonCopyable对象,我们使用std::forward_as_tuple来构建拥有一个元组参考NonCopyable对象。该引用被转发到std::pair的构造函数,后者又将其转发到UsesNonCopyable的构造函数。


请注意,std::map::try_emplace只要您的密钥类型是可复制的,C++17 的添加就消除了大部分这种复杂性。以下将工作得很好并且明显更简单:

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