让我们说您有一个带有std::tuple
的可变参数类,可以使用args + 1个新arg进行构造。使用std::apply()
和原始花括号构造函数构造时,该构造函数不会返回右值。这意味着该类不是可移动构造的。下面是一个例子来澄清。
#include <cstdio>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <vector>
template <class... Args>
struct icecream {
icecream() = default;
template <class... MoreArgs>
icecream(icecream<MoreArgs...>&& ice) {
std::apply(
[this](auto&&... ds) {
data = { std::move(ds)..., {} };
},
std::move(ice.data));
}
// This works :
// template <class... MoreArgs>
// icecream(icecream<MoreArgs...>&& ice) {
// std::apply(
// [this](auto&&... ds) {
// data = { std::move(ds)...,
// std::move(std::vector<double>{}) };
// },
// std::move(ice.data));
// }
std::tuple<std::vector<Args>...> data{};
};
int main(int, char**) {
icecream<int> miam;
std::get<0>(miam.data).push_back(1);
std::get<0>(miam.data).push_back(2);
icecream<int, double> cherry_garcia{ std::move(miam) };
printf("miam : \n");
for (const auto& x : std::get<0>(miam.data)) {
printf("%d\n", x);
}
printf("\ncherry_garcia : \n");
for (const auto& x : std::get<0>(cherry_garcia.data)) {
printf("%d\n", x);
}
return 0;
}
输出为:
miam :
1
2
cherry_garcia :
1
2
该示例有些笨拙,但说明了这一点。在第一个move构造函数中,使用{}
和元组副本构造。如果您用硬编码的std::move()
取消注释第二个构造函数,那么它将起作用。
我测试VS最新,clang最新和gcc最新。全部具有相同的结果。 (wandbox:https://wandbox.org/permlink/IQqqlLcmeyOzsJHC)
问题是,为什么不返回右值?我显然缺少卷曲的构造函数。这可能与可变参数无关,但我想我也应该展示真实的情况。
答案 0 :(得分:7)
为什么原始的卷曲构造函数{}不返回右值?
问题是另一个。
问题是
data = { std::move(ds)..., {} };
调用“直接构造函数”(this page中的构造函数(2)),
constexpr tuple( const Types&... args ); (2)
不是“转换构造函数”(构造函数(3))
template< class... UTypes > constexpr tuple( UTypes&&... args ); (3)
您期望的。
问题在于,对于编译器而言,“ {}
”不足以推导类型(构造函数(3)中UTypes...
列表的最后一个类型),因此构造函数(3)被排除,编译器选择构造函数(2)。
Whit构造函数(2),可以使用“ {}
”构造列表中Types...
的最后一种对象,因为Types...
是已知的并且不可以推论。
但是构造函数(2)是复制构造函数(从元组的Types...
角度来看),而不是正向构造函数作为构造函数(3),因此第一个向量是复制的,而不是移动的。
打电话时不一样
data = { std::move(ds)..., std::move(std::vector<double>{}) };
还是
data = { std::move(ds)..., std::vector<double>{} };
因为最后一个参数可以明确推导为std::vector<double>{} &&
,所以编译器调用“转换构造函数”(构造函数(3))并移动第一个向量的内容。
非主题:仅当std::vector<double>{}
是double
中的最后一个类型时才使用Args...
,我建议使用{{1} }。
此外,我建议SFINAE仅在std::tuple_element
时启用您的构造函数。
也许也sizeof...(MoreArgs)+1u == sizeof...(Args)
(启用完美转发),而不是std::forward()
在lambda中。
所以我建议以下构造函数
std::move()