Я попытался скомпилировать следующие фрагменты с 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< > :: кортеж (_UElements&& …) [с _UElements = {int, double, char}; = пустота; _Elements = {int, double, char}] ’
Информация об ошибках, передаваемая компилятором, является для меня полной ошибкой, и я не знаю, как реализованы конструкторы кортежей, но я точно знаю, что они полностью подходят для равномерной инициализации (например: tuple<int,float,char>{1,2.2,'X'}
), поэтому мне интересно, является ли проблема, с которой я столкнулся, только TODO компилятора, или это что-то определенное стандартом C ++ 11.
Любой ответ будет оценен!
Подходящий std::tuple
конструкторы explicit
, Это означает, что то, что вы хотите сделать, невозможно, поскольку синтаксис, который вы хотите использовать, определяется с точки зрения инициализации копирования (что запрещает вызывать explicit
конструктор). По сравнению, std::tuple<int, float, char> { 1, 2.2, 'X' }
использует прямую инициализацию. std::pair
имеет неexplicit
только конструкторы.
Используйте либо прямую инициализацию, либо одну из стандартных функций фабрики кортежей (например, std::make_tuple
).
Это на самом деле выполнимо, с функциями 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} в initializer_list (или вектор, который неявно преобразует initializer_list) типа MultiTypeWrapper. Это означает, что мы не можем написать такую функцию, как ниже принять такой 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
С ++ не поддерживает специализацию функций. Теперь, если мы хотим проверить код:
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
Надеюсь, мой вклад поможет 🙂