C++ 元组向量和 initializer_list
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12436586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
tuple vector and initializer_list
提问by Need4Steed
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}; = void; _Elements = {int, double, char}]'
错误:从初始值设定项列表转换为 'std::tuple' 将使用显式构造函数 'constexpr std::tuple< >::tuple(_UElements&& ...) [with _UElements = {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 标准定义的东西。
回答by Luc Danton
The relevant std::tuple
constructors are 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). In contrast, std::tuple<int, float, char> { 1, 2.2, 'X' }
uses direct initialization. std::pair
does have non-explicit
constructors only.
相关的std::tuple
构造函数是explicit
. 这意味着您想要做的事情是不可能的,因为您想要使用的语法是根据复制初始化(禁止调用explicit
构造函数)定义的。相反,std::tuple<int, float, char> { 1, 2.2, 'X' }
使用直接初始化。std::pair
确实只有非explicit
构造函数。
Either use direct-initialization or one of the Standard tuple factory function (e.g. std::make_tuple
).
使用直接初始化或标准元组工厂函数之一(例如std::make_tuple
)。
回答by BreakDS
This is actually doable, with c++11 features.
这实际上是可行的,具有 c++11 特性。
Yes the initializer_list wants all its element to be of the same type. The trick is that we can create a wrapper class that can be static_cast
to all the types we want. This is easy to achieve:
是的 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_;
};
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. This means that we can not write a function like belowto accept such intializer_list as argument:
使用隐式转换构造函数,我们可以将 {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) {
....
}
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. It's like a recursion on template arguments:
我们使用另一个技巧将向量中的每个值转换为其原始类型(请注意,我们在 的定义中提供了隐式转换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;
}
Note that we have to create the helper class for set_tuple
since c++ does not support function specialization. Now if we want to test the code:
请注意,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());
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 :)
希望我的意见有帮助:)