如何编写类似于std :: make_tuple的make_vector?

Tre*_*key 2 c++ templates variadic-templates perfect-forwarding c++11

而不是像这样创建矢量:

  std::vector<int>     v1{1,2,3};
  std::vector<double>  v2{1.1,2.2,3.3};
  std::vector<Object>  v3{Object{},Object{},Object{}};  
Run Code Online (Sandbox Code Playgroud)

我想用通用函数创建它们:

  auto v1 = make_vector(1,2,3);
  auto v2 = make_vector(1.1,2.2,3.3);
  auto v3 = make_vector(Object{},Object{},Object{}); 
Run Code Online (Sandbox Code Playgroud)

std :: make_pairstd :: make_tuple类似,这是我对vector的尝试:

#include <iostream>
#include <vector>
#include <utility>

template <typename... T>
auto make_vector(T&&... args)
{
    using first_type = typename std::tuple_element<0, std::tuple<T...>>::type;
    return std::vector<first_type>{std::forward<T>(args)...};
}
Run Code Online (Sandbox Code Playgroud)

它编译,但当我尝试使用它时:

auto vec = make_vector(1,2,3);  
Run Code Online (Sandbox Code Playgroud)
m.cpp: In instantiation of ‘auto make_vector(T&& ...) [with T = {int, int, int}]’:
m.cpp:16:30:   required from here
m.cpp:8:78: error: invalid use of incomplete type ‘class std::tuple_element<0ul, std::tuple<int, int, int> >’
     using first_type = typename std::tuple_element<0, std::tuple<T...>>::type;
                                                                              ^
In file included from m.cpp:3:0:
/usr/include/c++/5/utility:85:11: note: declaration of ‘class std::tuple_element<0ul, std::tuple<int, int, int> >’
     class tuple_element;
           ^
m.cpp:9:60: error: invalid use of incomplete type ‘class std::tuple_element<0ul, std::tuple<int, int, int> >’
     return std::vector<first_type>{std::forward<T>(args)...};
                                                            ^
In file included from m.cpp:3:0:
/usr/include/c++/5/utility:85:11: note: declaration of ‘class std::tuple_element<0ul, std::tuple<int, int, int> >’
     class tuple_element;
           ^
m.cpp: In function ‘int main()’:
m.cpp:16:30: error: ‘void v1’ has incomplete type
   auto v1 = make_vector(1,2,3);  
Run Code Online (Sandbox Code Playgroud)

如何创建一个通用例程,
它使用第一个参数的第一个类型来实例化向量?
如何将参数作为初始化值转发给向量?

Bar*_*rry 9

由于无论如何都不能使用它来创建空向量,我们可以tuple通过提供额外的模板参数来避免依赖:

template <class T0, class... Ts>
auto make_vector(T0&& first, Ts&&... args)
{
    using first_type = std::decay_t<T0>;
    return std::vector<first_type>{
        std::forward<T0>(first),
        std::forward<Ts>(args)...
    };
}
Run Code Online (Sandbox Code Playgroud)

如果first作为左值传入,它具有工作的额外好处.


T.C*_*.C. 5

让我们跟随make_array,让用户明确指定返回类型,或使用由确定的返回类型std::common_type

template<class T> struct identity { using type = T; };
template<class D, class... Ts>
struct ret : identity<D> {};
template<class... Ts>
struct ret<void, Ts...> : std::common_type<Ts...> {};
template<class D, class... Ts>
using ret_t = typename ret<D, Ts...>::type;

template<class D = void, class... Ts>
std::vector<ret_t<D, Ts...>> make_vector(Ts&&... args) {
    std::vector<ret_t<D, Ts...>>  ret;
    ret.reserve(sizeof...(args));
    using expander = int[];
    (void) expander{ ((void)ret.emplace_back(std::forward<Ts>(args)), 0)..., 0 };
    return ret;
}
Run Code Online (Sandbox Code Playgroud)

使用完美转发建议您要消除不必要的复制;这与使用initalizer_list需要每个元素都有副本的构造函数不一致。因此,上述代码替代了reserve正确的空间量,然后emplace_back使用通常的包扩展技巧一一对应化元素。

push_back如果您不想启用显式转换,则可以使用,而可能会因类型不匹配而产生潜在的后果。但是,在这种情况下,类型可以由用户显式指定,也可以通过隐式转换由来推导common_type,因此emplace_back就可以了。


Pix*_*ist 2

就像你所做的那样 - gcc 和 msvc 都用一个微小的#include <tuple>.