32

我尝试用 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'}};

但是,对于元组的向量,编译器会抱怨:

错误:从初始化列表转换为 'std::tuple' 将使用显式构造函数 'constexpr std::tuple< >::tuple(_UElements&& ...) [with _UElements = {int, double, char}; =无效;_Elements = {int, double, char}]'</p>

编译器溢出的错误信息对我来说完全是胡言乱语,我不知道元组的构造函数是如何实现的,但我知道它们完全可以使用统一初始化(如:)tuple<int,float,char>{1,2.2,'X'},因此,我想知道问题是否我遇到的只是编译器的 TODO 或者它是 C++11 标准定义的东西。

4

4 回答 4

13

相关的std::tuple构造函数是explicit. 这意味着您想要做的事情是不可能的,因为您要使用的语法是根据复制初始化(禁止调用explicit构造函数)定义的。相反,std::tuple<int, float, char> { 1, 2.2, 'X' }使用直接初始化。std::pair确实只有非explicit构造函数。

使用直接初始化或标准元组工厂函数之一(例如std::make_tuple)。

于 2012-09-15T11:04:49.657 回答
2

这实际上是可行的,具有 c++11 特性。

是的,initializer_list 希望它的所有元素都属于同一类型。诀窍是我们可以创建一个包装类,它可以是static_cast我们想要的所有类型。这很容易实现:

 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_;
 };

使用隐式转换构造函数,我们可以将类似 {1,2.5,'c',4} 的内容传递给 MultiTypeWrapper 类型的 initializer_list(或向量,它隐式转换 initializer_list)。这意味着我们不能编写像下面这样的函数来接受这样的 intializer_list 作为参数:

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

我们使用另一个技巧将向量中的每个值转换为其原始类型(请注意,我们在 的定义中提供了隐式转换MultiTypeWrapper)并将其分配给元组中的相应槽。这就像模板参数的递归:

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;
}

请注意,我们必须创建帮助程序类,set_tuple因为 c++ 不支持函数特化。现在,如果我们要测试代码:

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());

输出将是:

1 2.50 ABC

这是在我的桌面上用 clang 3.2 测试的

希望我的意见有帮助:)

于 2013-08-28T05:31:03.167 回答
0

是不是很烦人?我以前也使用过对 - 在类似的场景中,很惊讶元组不支持这一点,因为 {} 初始化语法节省了很多混乱。您可以使用make_tuple在容器中手动插入元素,因此:

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

应该管用

于 2020-06-04T12:39:10.080 回答
0

您不能仅将 accolades 用于初始化元组,而必须使用关键字 tuple

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

C++ 11

于 2021-09-01T22:41:11.280 回答