scx*_*scx 2 c++ templates variadic-templates generic-lambda c++17
假设你有一个带有a的可变类std::tuple,可以使用args + 1 new arg构造.当使用std::apply()和原始花括号构造函数构造时,该构造函数不返回rvalue.这意味着班级不是移动构造的.下面举一个例子来澄清.
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
miam :
1
2
cherry_garcia :
1
2
Run Code Online (Sandbox Code Playgroud)
这个例子有点愚蠢,但说明了这一点.在第一个移动构造函数中,{}使用和元组复制构造.如果您使用硬编码取消注释第二个构造函数std::move(),那么它可以正常工作.
我测试VS最新,clang最新和gcc最新.所有都有相同的结果.(wandbox:https://wandbox.org/permlink/IQqqlLcmeyOzsJHC )
所以问题是,为什么不返回右值?我显然错过了卷曲构造函数.这可能与可变参数无关,但我想我也可以展示真实场景.
为什么原始卷曲构造函数{}不返回右值?
问题是另一个问题.
问题是
data = { std::move(ds)..., {} };
Run Code Online (Sandbox Code Playgroud)
调用"直接构造函数"(此页面中的构造函数(2)),
Run Code Online (Sandbox Code Playgroud)constexpr tuple( const Types&... args ); (2)
不是"转换构造函数"(构造函数(3))
Run Code Online (Sandbox Code Playgroud)template< class... UTypes > constexpr tuple( UTypes&&... args ); (3)
你期待的.
问题是{},对于编译器来说," "不足以推导出一种类型(UTypes...构造函数(3)中列表的最后一种类型),因此构造函数(3)被排除,编译器选择构造函数(2).
Whit构造函数(2)," {}"可以构造Types...列表的最后一种类型的对象,因为它Types...是已知的而不是被推导出来的.
但构造函数(2)是一个复制构造函数(从Types...元组的角度来看),而不是构造函数(3)的正构造函数,因此复制第一个向量,而不是移动.
你打电话的时候会有所不同
data = { std::move(ds)..., std::move(std::vector<double>{}) };
Run Code Online (Sandbox Code Playgroud)
或者也
data = { std::move(ds)..., std::vector<double>{} };
Run Code Online (Sandbox Code Playgroud)
因为最后一个参数可以清楚地推导出来,std::vector<double>{} &&因此编译器调用"转换构造函数"(构造函数(3))并移动第一个向量的内容.
关闭主题:代替使用std::vector<double>{},仅在double最后一个类型中有效时Args...,我建议使用更通用的代码std::tuple_element.
此外,我建议SFINAE只在启用你的构造函数时sizeof...(MoreArgs)+1u == sizeof...(Args).
也许也std::forward()(启用完美转发)而不是std::move()lambda内部.
所以我建议以下构造函数
template <typename ... MoreArgs,
std::enable_if_t<sizeof...(MoreArgs)+1u == sizeof...(Args)> * = nullptr>
icecream(icecream<MoreArgs...>&& ice) {
std::apply(
[this](auto && ... ds) {
data = { std::forward<decltype(ds)>(ds)...,
std::tuple_element_t<sizeof...(Args)-1u,
decltype(data)>{} };
},
std::move(ice.data));
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
135 次 |
| 最近记录: |