将std :: tuple转换为模板参数包

Olo*_*lof 9 c++ tuples variadic-templates c++11

我有一个

typedef std::tuple<A, B> TupleType;
Run Code Online (Sandbox Code Playgroud)

并希望将类列表用于"模板".

假设我有:

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

并且我可以成功地使用它:

auto my_tuple = parse<A, B>(ifs);
Run Code Online (Sandbox Code Playgroud)

是否可以避免必须指定类列表A,B如果我已经有了

typedef std::tuple<A,B> TupleType;
Run Code Online (Sandbox Code Playgroud)

列表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;
}
Run Code Online (Sandbox Code Playgroud)

jog*_*pan 6

您的情况中的潜在问题似乎是,parse当模板参数为a时,您希望专门针对特殊情况使用函数模板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); }
};
Run Code Online (Sandbox Code Playgroud)

然后,您可以按所需方式调用它:

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

作为第二步,您可以定义一个函数模板(再次),它将参数传递给结构的右侧特化:

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

现在您可以按照您想要的方式使用它:

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

(你仍然可以用旧的方式使用它:auto tup = parse<A,B>(std::cin).)


备注.正如在解析器:: parse()的注释中所提到的,我使用直接元组构造而不是make_tuple为了避免元组元素的构造顺序的问题.这与您的问题没有直接关系,但这是一件好事.了解如何在使用std :: make_tuple时避免构造函数的未定义执行顺序.