有了std::apply()的介绍,这就很直接了:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems){
return std::vector<T>{std::forward<decltype(elems)>(elems)...};
}, std::forward<Tuple>(tuple));
}
std::apply() 是一个 C++17 函数,但可以在 C++14 中实现(请参阅链接了解可能的实现)。作为改进,您可以添加 SFINAE 或 static_assert,以使元组中的所有类型实际上都是 T。
正如T.C. 指出的那样,这会导致每个元素的额外副本,因为std::initializer_list 由const 数组支持。那真不幸。我们在不必对每个元素都进行边界检查的情况下赢得了一些胜利,但在复制时却失去了一些。复制最终过于昂贵,另一种实现方式是:
template <class Tuple,
class T = std::decay_t<std::tuple_element_t<0, std::decay_t<Tuple>>>>
std::vector<T> to_vector(Tuple&& tuple)
{
return std::apply([](auto&&... elems) {
using expander = int[];
std::vector<T> result;
result.reserve(sizeof...(elems));
expander{(void(
result.push_back(std::forward<decltype(elems)>(elems))
), 0)...};
return result;
}, std::forward<Tuple>(tuple));
}
请参阅this answer 了解扩展器技巧的说明。请注意,我放弃了领先的0,因为我们知道包是非空的。在 C++17 中,使用折叠表达式会变得更简洁:
return std::apply([](auto&&... elems) {
std::vector<T> result;
result.reserve(sizeof...(elems));
(result.push_back(std::forward<decltype(elems)>(elems)), ...);
return result;
}, std::forward<Tuple>(tuple));
虽然仍然相对不如initializer_list 构造函数好。不幸的。