[英]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.