简体   繁体   English

元组向量和 initializer_list

[英]tuple vector and initializer_list

I tried to compile the following snippets with gcc4.7我试图用 gcc4.7 编译以下代码片段

vector<pair<int,char> > vp = {{1,'a'},{2,'b'}};
//For pair vector, it works like a charm.

vector<tuple<int,double,char> > vt = {{1,0.1,'a'},{2,4.2,'b'}};

However, for the vector of tuples, the compiler complains:但是,对于元组向量,编译器会抱怨:

error: converting to 'std::tuple' from initializer list would use explicit constructor 'constexpr std::tuple< >::tuple(_UElements&& ...) [with _UElements = {int, double, char};错误:从初始值设定项列表转换为 'std::tuple' 将使用显式构造函数 'constexpr std::tuple< >::tuple(_UElements&& ...) [with _UElements = {int, double, char}; = void; = 无效; _Elements = {int, double, char}]' _Elements = {int, double, char}]'

The error info spilled by the compiler is total gibberish for me, and I have no idea how were the constructors of tuple implemented, yet I do know they're totally okay with uniform initialization (like: tuple<int,float,char>{1,2.2,'X'} ), therefore, I wonder if the problem I encountered is only a TODO of the compiler or it's something defined by the C++11 standard.编译器溢出的错误信息对我来说完全是胡言乱语,我不知道元组的构造函数是如何实现的,但我知道它们完全可以统一初始化(例如: tuple<int,float,char>{1,2.2,'X'} ),因此,我想知道我遇到的问题只是编译器的 TODO 还是 C++11 标准定义的问题。

The relevant std::tuple constructors are explicit .相关的std::tuple构造函数是explicit This means that what you want to do is not possible, since the syntax you want to use is defined in terms of copy initialization (which forbids calling an explicit constructor).这意味着您想要做的事情是不可能的,因为您想要使用的语法是根据复制初始化(禁止调用explicit构造函数)定义的。 In contrast, std::tuple<int, float, char> { 1, 2.2, 'X' } uses direct initialization.相反, std::tuple<int, float, char> { 1, 2.2, 'X' }使用直接初始化。 std::pair does have non- explicit constructors only. std::pair确实只有非explicit构造函数。

Either use direct-initialization or one of the Standard tuple factory function (eg std::make_tuple ).使用直接初始化或标准元组工厂函数之一(例如std::make_tuple )。

This is actually doable, with c++11 features.这实际上是可行的,具有 c++11 特性。

Yes the initializer_list wants all its element to be of the same type.是的 initializer_list 想要它的所有元素都是相同的类型。 The trick is that we can create a wrapper class that can be static_cast to all the types we want.诀窍是我们可以创建一个包装类,该类可以将static_cast转换为我们想要的所有类型。 This is easy to achieve:这很容易实现:

 template <typename... tlist>
 class MultiTypeWrapper {
 };

 template <typename H>
 class MultiTypeWrapper<H> {
 public:
   MultiTypeWrapper() {}

   MultiTypeWrapper(const H &value) : value_(value) {}

   operator H () const {
     return value_;
   }
 private:
   H value_;
 };

 template <typename H, typename... T>
 class MultiTypeWrapper<H, T...> 
   : public MultiTypeWrapper<T...> {

 public:
   MultiTypeWrapper() {}

   MultiTypeWrapper(const H &value) : value_(value) {}

   // If the current constructor does not match the type, pass to its ancestor.
   template <typename C>
   MultiTypeWrapper(const C &value) : MultiTypeWrapper<T...>(value) {}

   operator H () const {
     return value_;
   }
 private:
   H value_;
 };

With the implicit conversion constructors, we can pass something like {1,2.5,'c',4} to an initializer_list (or vector, which implicitly converts the initializer_list) of type MultiTypeWrapper.使用隐式转换构造函数,我们可以将 {1,2.5,'c',4} 之类的内容传递给 MultiTypeWrapper 类型的 initializer_list(或隐式转换 initializer_list 的向量)。 This means that we can not write a function like below to accept such intializer_list as argument:这意味着我们不能编写像下面这样的函数来接受这样的 intializer_list 作为参数:

template <typename... T>
std::tuple<T...> create_tuple(std::vector<unit_test::MultiTypeWrapper<T...> > init) {
  ....
}

We use another trick to cast each value in the vector to its original type (note that we provide implicit conversion in the definition of MultiTypeWrapper ) and assign it to the corresponding slot in a tuple.我们使用另一个技巧将向量中的每个值转换为其原始类型(请注意,我们在MultiTypeWrapper的定义中提供了隐式转换)并将其分配给元组中的相应插槽。 It's like a recursion on template arguments:这就像模板参数的递归:

template <int ind, typename... T>
class helper {
public:
  static void set_tuple(std::tuple<T...> &t, const std::vector<MultiTypeWrapper<T...> >& v) {
    std::get<ind>(t) = static_cast<typename std::tuple_element<ind,std::tuple<T...> >::type>(v[ind]);
    helper<(ind-1),T...>::set_tuple(t,v);
  }
};



template <typename... T>
class helper<0, T...> {
public:
  static void set_tuple(std::tuple<T...> &t, const std::vector<MultiTypeWrapper<T...> >& v) {
    std::get<0>(t) = static_cast<typename std::tuple_element<0,std::tuple<T...> >::type>(v[0]);
  }
};



template <typename... T>
std::tuple<T...> create_tuple(std::vector<unit_test::MultiTypeWrapper<T...> > init) {
  std::tuple<T...> res;
  helper<sizeof...(T)-1, T...>::set_tuple(res, init);
  return res;
}

Note that we have to create the helper class for set_tuple since c++ does not support function specialization.请注意,我们必须为set_tuple创建辅助类,因为 C++ 不支持函数专业化。 Now if we want to test the code:现在,如果我们要测试代码:

auto t = create_tuple<int,double,std::string>({1,2.5,std::string("ABC")});
printf("%d %.2lf %s\n", std::get<0>(t), std::get<1>(t), std::get<2>(t).c_str());

The output would be:输出将是:

1 2.50 ABC

This is tested on my desktop with clang 3.2这是在我的桌面上用 clang 3.2 测试的

Hope my input helps :)希望我的意见有帮助:)

Annoying isn't it?是不是很烦人? I was also using pairs before - in a similar scenario and was surprised tuples do not support this, as the {} initialization syntax saves a lot of clutter.我之前也使用过对 - 在类似的场景中,很惊讶元组不支持这一点,因为 {} 初始化语法节省了很多混乱。 You can insert the elements manually in containers using make_tuple , so:您可以使用make_tuple在容器中手动插入元素,因此:

vt.push_back(make_tuple(2,4.2,'b'));

should work应该管用

you can't use the accolades just for initializing the tuple, you must use the keyword tuple instead您不能仅将荣誉用于初始化元组,而必须使用关键字 tuple

vector<tuple<int, int>> my_vec{
    tuple<int, int> { 1, 15 },
    tuple<int, int> { 2, 100 }
};

C++ 11 C++ 11

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 对initializer_list visual 2017的向量 - vector of pair initializer_list visual 2017 转换向量<t>到 initializer_list<t></t></t> - Convert a vector<T> to initializer_list<T> C ++ Initializer_List进入指针向量 - C++ Initializer_List into Vector of Pointers 如何将 std::initializer_list 分配给向量 - how to assign std::initializer_list to a vector 为什么`initializer_list <pair> `和`initializer_list <tuple> `表现不一样? - Why do `initializer_list<pair>` and `initializer_list<tuple>` behave differently? 在向量的初始化中合并向量和初始化列表 <vector<T> &gt;? - Merge vector and initializer_list in initialization of vector<vector<T>>? 在编译时将initializer_list <T>转换为initializer_list <vector <T >> - convert initializer_list<T> to initializer_list<vector<T>> at compile time 从 initializer_list 转换<const char*>初始化器列表<string>在向量构造函数中</string></const> - Conversion from initializer_list<const char*> to initializer_list<string> in vector constructor 如何同时接受 std::vector 和 std::initializer_list - How to accept both std::vector and std::initializer_list 将initializer_list插入std :: vector时“无效的迭代器范围” - “Invalid iterator range” while inserting initializer_list to an std::vector
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM