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'});
最后,works。 std::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::pair
的std::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'});
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'});
最后,works。 std::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::pair
的std::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'});