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::pairexplicitコンストラクターのみがあります。

直接初期化または標準のタプル ファクトリ関数のいずれかを使用します (例: 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_tupleC++ は関数の特殊化をサポートしていないため、ヘルパー クラスを作成する必要があることに注意してください。コードをテストしたい場合:

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 に答える