10

私は

typedef std::tuple<A, B> TupleType;

そして、「テンプレート」のクラスのリストを使用したいと思います。

私が持っているとしましょう:

template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
  return std::make_tuple(args(stream)...);
}

そして、私はそれをうまく使うことができます:

auto my_tuple = parse<A, B>(ifs);

すでにクラスリストA、Bを持っている場合、クラスリストA、Bを指定する必要を回避することは可能ですか?

typedef std::tuple<A,B> TupleType;

リストA、Bはすでに存在しますか?

例:

#include <cstdlib>  // EXIT_SUCCESS, EXIT_FAILURE
#include <iostream> // std::cerr
#include <fstream>  // std::ifstream
#include <tuple>    // std::tuple

class A {
public:
  A(std::istream &);  // May throw FooBaarException 
};

class B {
public:
  B(std::istream &); // May throw FooBaarException 
};

template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
  return std::make_tuple(args(stream)...);
}

int main() {
  std::ifstream ifs;
  ifs.exceptions(ifstream::eofbit | ifstream::failbit | ifstream::badbit);
  int res = EXIT_FAILURE;
  try {
    ifs.open("/some/file/path", std::ios::in | std::ios::binary);
    auto my_tuple = parse<A, B>(ifs); // my_tuple is of the type std::tuple<A,B>
    /* Here do something interesting with my_tuple */ 
    res = EXIT_SUCCESS;
  } catch (ifstream::failure e) {
    std::cerr << "error: opening or reading file failed\n";
  } catch (FooBaarException e) {
    std::cerr << "error: parsing in a constructor failed\n";
  }
  return res;
}
4

3 に答える 3

7

あなたの状況の根本的な問題はparse、テンプレート引数がstd::tuple. 残念ながら、この種の特殊化は関数テンプレートでは不可能です。

ただし、クラス テンプレートでは可能です。

parseしたがって、最初のステップとして、次のように a の静的関数として定義できますstruct

using std::istream;
using std::tuple;
using std::make_tuple;

struct A { A(const istream &) {} };
struct B { B(const istream &) {} };

template <typename... Args>
struct parser
{
  /* Your original function, now inside a struct.
     I'm using direct tuple construction and an
     initializer list to circumvent the order-of-
     construction problem mentioned in the comment
     to your question. */
  static tuple<Args...> parse(const istream &strm)
  { return tuple<Args...> {Args(strm)...}; }
};

template <typename... Args>
struct parser<tuple<Args...>>
{
  /* Specialized for tuple. */
  static tuple<Args...> parse(const istream &strm)
  { return parser<Args...>::parse(strm); }
};

その後、目的の方法で呼び出すことができます。

int main()
{
  typedef tuple<A,B> tuple_type;
  auto tup = parser<tuple_type>::parse(std::cin);
  return 0;
}

2 番目のステップとして、構造体の適切な特殊化に引数を渡す関数テンプレートを (再び) 定義できます。

template <typename... Args>
auto parse(const istream &strm) -> decltype(parser<Args...>::parse(strm))
{ return parser<Args...>::parse(strm); }

そして今、まさにあなたが望んでいた方法でそれを使用することができます:

int main()
{
  typedef tuple<A,B> tuple_type;
  auto tup = parse<tuple_type>(std::cin);
  return 0;
}

(そして、古い方法でも引き続き使用できます: auto tup = parse<A,B>(std::cin).)


述べる。parser::parse() へのコメントで述べたようにmake_tuple、タプル要素の構築順序に関する問題を回避する代わりに、直接タプル構築を使用しました。これはあなたの質問とは直接関係ありませんが、良いことです。std::make_tuple を使用するときに、コンストラクターの未定義の実行順序を回避する方法を参照してください。

于 2012-12-26T01:57:39.443 に答える
2

この種のものには標準的なイディオムがあります。[1]

// Define the "shape" of the template
template<typename Tuple> struct TupleMap;
// Specialize it for std::tuple
template<typename...T> struct TupleMap<std::tuple<T...>> {
  using type = std::tuple<T...>;  // not necessary but saves typing
  // ... inside here, you have access to the parameter pac
}

これを使用する例を次に示します。これは、期待に合う場合と合わない場合があります(typedef質問で約束した内容がないため、この例は実際には期待される使用法を示していません):liveworkspace.org

litbがポイントを上げたので、タプルコンポーネントを左から右の順序で構築するように強制することができ、別の興味深いイディオムであるコームの継承を示しています。lwsを参照してください。

(lwsが再び消える可能性があるので、誰が知っているので、ここにもコードを貼り付けます):

#include <iostream>
#include <tuple>
#include <type_traits>
#include <utility>

// Define the "shape" of the template
template<typename Tuple> struct TupleMap;
// Specialize it for std::tuple
template<typename...T> struct TupleMap<std::tuple<T...>> {
   using type = std::tuple<T...>;  // not necessary but saves typing

   type value;

   template<typename Arg>
   TupleMap(Arg&& arg)
       : value(T(std::forward<Arg>(arg))...) {
   }

   operator type() { return value; }
};

//Try it out:
using std::get;  // Note 2
using Numbers = std::tuple<char, double, int>;

// Note 3
std::ostream& operator<<(std::ostream& out, const Numbers& n) {
   return out << get<0>(n) << ' ' << get<1>(n) << ' ' << get<2>(n);
}

int main() {
    std::cout << TupleMap<Numbers>(93.14159);
    return 0;
}

[1]少なくとも、それは標準的なイディオムだと思います。私はよく使っており、「缶切り」のパターンだと思っています。

get[2]これは、の外部で定義されたタプルのようなテンプレートで使用できるようにするために必要です(または少なくとも私のスタイルです)std。このようにすることで、ADLは、getに特殊化を追加することなく、の適切な定義を見つけることができますstd::get。このように、との標準的なADLイディオムに似ていbeginますend

[3] SOを検索してoperator<<、すべてのタプルに特化したクールなハックを探すことができます。特定のタプルに使用できるより単純なものがありますが、それはすべてこの質問のトピックから外れているので、私は簡単で依存関係のないことをしました。これは、の変換演算子のために機能することに注意してくださいTupleMap

于 2012-12-25T22:37:13.227 に答える
1

基本的なアプローチは、一連のインデックス0, ..., std::tuple_size<Tuple>::value - 1をパラメーター パックとして作成しIndices、関数を .xml で呼び出すことparse<typename std::tuple_element<Tuple, Indices>::type...>(stream)です。おそらく、ロジックを関数parse_tuple<Tuple>(stream)(およびこれが委任する関数) にカプセル化し、最終的に に委任しparse<...>(stream)ます。

まず、クラス テンプレートと、 のサイズに基づいて一連のインデックスを作成する関数を次に示しますstd::tuple。からタイプのリストを取得するには、インデックスが必要ですstd::tuple

template <int... Indices> struct indices;
template <> 
struct indices<-1> {                // for an empty std::tuple<> there is no entry
    typedef indices<> type;
};
template <int... Indices>
struct indices<0, Indices...> {     // stop the recursion when 0 is reached
    typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...> { // recursively build a sequence of indices
    typedef typename indices<Index - 1, Index, Indices...>::type type;
};

template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices() {
    return 0;
}

これが整っていれば、 a から型のシーケンスを抽出するのは非常に簡単ですstd::tuple<T...>:

template<typename T, int... Indices>
T parse_tuple(std::istream &stream, indices<Indices...> const*) {
    return parse<typename std::tuple_element<Indices, T>::type...>(stream);
}
template <typename T>
T parse_tuple(std::istream& stream) {
    return parse_tuple<T>(stream, make_indices<T>());
}
于 2012-12-25T20:48:19.977 に答える