gpt4 book ai didi

c++ - 为什么原始 curl 构造函数 {} 不返回右值?

转载 作者:搜寻专家 更新时间:2023-10-30 23:52:00 24 4
gpt4 key购买 nike

假设您有一个带有 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

这个例子有点简单,但说明了这一点。在第一个移动构造函数中,使用了 {} 并构造了元组拷贝。如果您使用硬编码的 std::move() 取消注释第二个构造函数,那么它就可以工作。

我在 VS latest、clang latest 和 gcc latest 上测试。都有相同的结果。 (魔杖盒:https://wandbox.org/permlink/IQqqlLcmeyOzsJHC)

那么问题来了,为什么不返回一个右值呢?我显然在 curly 构造函数中遗漏了一些东西。这可能与可变参数无关,但我想我不妨展示真实的场景。

最佳答案

Why doesn't raw curly constructor {} return an rvalue?

问题是另一个。

问题是

data = { std::move(ds)..., {} };

调用“直接构造函数”(this page 中的构造函数 (2)),

constexpr tuple( const Types&... args );       (2)

不是“转换构造函数”(constructor (3))

template< class... UTypes >
constexpr tuple( UTypes&&... args ); (3)

如你所料。

问题是“{}”对于编译器来说还不足以推导出一个类型(构造函数 (3) 中 UTypes... 列表的最后一个类型),因此构造函数 (3) 被排除在外,并且编译器选择构造函数(2)。

Whit constructor (2), "{} "可以构造一个 Types... 的最后一个类型的对象列表中的 Types...是已知的而不是被推断的。

但是构造函数 (2) 是复制构造函数(从元组的 Types... 的角度来看),而不是构造函数 (3) 的正向构造函数,因此第一个 vector 被复制,而不是移动。

打电话的时候不一样

data = { std::move(ds)..., std::move(std::vector<double>{}) };

或者还有

data = { std::move(ds)..., std::vector<double>{} };

因为最后一个参数可以清楚地推断为std::vector<double>{} &&因此编译器调用“转换构造函数”(constructor (3))并移动第一个 vector 的内容。

题外话:而不是使用 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));
}

关于c++ - 为什么原始 curl 构造函数 {} 不返回右值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52050984/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com