将 std::tuple 转换为模板参数包
我有一个
typedef std::tuple<A, B> TupleType;
并且想使用类列表对于模板".
and would like to use the list of classes for a "template".
假设我有:
template<typename... args>
std::tuple<args...> parse(std::istream &stream) {
return std::make_tuple(args(stream)...);
}
并且我可以成功地使用它:
and that I can successfully use it with:
auto my_tuple = parse<A, B>(ifs);
如果我已经有一个,是否可以避免指定类列表 A,B
is it possible to avoid having to specify the class list A,B if I already have a
typedef std::tuple<A,B> TupleType;
列表 A,B 已经在哪里了?
where the list A,B is already present?
一个例子:
#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
";
} catch (FooBaarException e) {
std::cerr << "error: parsing in a constructor failed
";
}
return res;
}
推荐答案
您的情况的根本问题似乎是您希望将函数模板 parse
专门用于以下特殊情况:模板参数是一个 std::tuple
.不幸的是,函数模板无法实现这种专业化.
The underlying problem in your situation seems to be that you'd like to specialize the function template parse
for the special case when the template argument is a std::tuple
. Unfortunately, this kind of specialization isn't possible with function templates.
但是,可以使用类模板.
However, it is possible with class templates.
因此,作为第一步,您可以将 parse
定义为 struct
的静态函数,如下所示:
So, as a first step, you could define parse
as a static function of a struct
, like this:
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); }
};
然后你可以用你想要的方式调用它:
You can then call it in the desired way:
int main()
{
typedef tuple<A,B> tuple_type;
auto tup = parser<tuple_type>::parse(std::cin);
return 0;
}
作为第二步,您可以(再次)定义一个函数模板,它将参数传递给结构的正确特化:
As a second step, you can define a function template (again) which passes the arguments on to the right specialization of the struct:
template <typename... Args>
auto parse(const istream &strm) -> decltype(parser<Args...>::parse(strm))
{ return parser<Args...>::parse(strm); }
现在你可以按照你想要的方式使用它了:
And now you can use it in exactly the way you wanted:
int main()
{
typedef tuple<A,B> tuple_type;
auto tup = parse<tuple_type>(std::cin);
return 0;
}
(您仍然可以以旧方式使用它:auto tup = parse<A,B>(std::cin)
.)
(And you can still use it in the old way, too: auto tup = parse<A,B>(std::cin)
.)
备注.正如 parser::parse() 的评论中提到的,我使用直接元组构造而不是 make_tuple
来避免元组元素的构造顺序问题.这与您的问题没有直接关系,而是一件好事.请参阅 如何避免未定义的执行顺序使用 std::make_tuple 时的构造函数.
Remark. As mentioned in the comment to parser::parse(), I used direct tuple construction instead of make_tuple
to avoid problems with the order of construction of the tuple elements. This is not directly related to your question, but a good thing to do. See how to avoid undefined execution order for the constructors when using std::make_tuple.
相关文章