今天,我遇到了这样一种情况:我有一个元组向量,其中元组可能包含多个条目。现在,我想将元组的向量转换为对象的向量,以使元组的条目与我的对象的统一初始化完全匹配。
以下代码为我完成了工作,但是有点笨拙。我在问自己,如果元组完全符合对象的统一初始化顺序,是否有可能派生出通用的解决方案来构造对象。
当要传递的参数数量增加时,这可能是非常理想的功能。
#include <vector>
#include <tuple>
#include <string>
#include <algorithm>
struct Object
{
std::string s;
int i;
double d;
};
int main() {
std::vector<std::tuple<std::string, int, double>> values = { {"A",0,0.},{"B",1,1.} };
std::vector<Object> objs;
std::transform(values.begin(), values.end(), std::back_inserter(objs), [](auto v)->Object
{
// This might get tedious to type, if the tuple grows
return { std::get<0>(v), std::get<1>(v), std::get<2>(v) };
// This is my desired behavior, but I don't know what magic_wrapper might be
// return magic_wrapper(v);
});
return EXIT_SUCCESS;
}
答案 0 :(得分:18)
提供Object
和std::tuple
的构造函数。您可以使用std::tie
来分配您的成员:
template<typename ...Args>
Object(std::tuple<Args...> t) {
std::tie(s, i, d) = t;
}
现在它会自动构建:
std::transform(values.begin(), values.end(), std::back_inserter(objs),
[](auto v) -> Object {
return { v };
});
要减少复制量,您可能需要将auto v
替换为const auto& v
,并使构造函数接受const std::tuple<Args...>& t
。
此外,通过const
迭代器访问源容器也是一种好习惯:
std::transform(
values.cbegin(), values.cend()
, std::back_inserter(objs), ...
答案 1 :(得分:13)
这是一种非侵入式版本(即不触碰Object
),可提取指定数据成员的数量。请注意,这依赖于聚合初始化。
template <class T, class Src, std::size_t... Is>
constexpr auto createAggregateImpl(const Src& src, std::index_sequence<Is...>) {
return T{std::get<Is>(src)...};
}
template <class T, std::size_t n, class Src>
constexpr auto createAggregate(const Src& src) {
return createAggregateImpl<T>(src, std::make_index_sequence<n>{});
}
您可以这样调用它:
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
[](const auto& v)->Object { return createAggregate<Object, 3>(v); });
或者,没有包装的lambda:
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
createAggregate<Object, 3, decltype(values)::value_type>);
正如@Deduplicator所指出的那样,以上帮助器模板实现了std::apply
的一部分,可以代替使用。
template <class T>
auto aggregateInit()
{
return [](auto&&... args) { return Object{std::forward<decltype(args)>(args)...}; };
}
std::transform(values.cbegin(), values.cend(), std::back_inserter(objs),
[](const auto& v)->Object { return std::apply(aggregateInit<Object>(), v); });
答案 2 :(得分:12)
从C ++ 17开始,您可以使用std::make_from_tuple:
std::transform(values.begin(),
values.end(),
std::back_inserter(objs),
[](const auto& t)
{
return std::make_from_tuple<Object>(t);
});
注意:需要Object
的适当构造函数。