[英]Tuple and variadic templates, how does this work?
我见过人们写(在堆栈溢出本身,询问一些甚至高级概念)的东西:
template<typename... args>
std::tuple<args...> parse(istream stream)
{
return std::make_tuple(args(stream)...);
}
并用它作为
auto tup = parse<int, float, char>(stream);
上面的代码如何通过解析流来构造元组? 是否有关于如何将数据放入流中的具体要求?
为此,必须有一个从std::istream
到指定为模板参数的所有类型的隐式转换。
struct A {
A(std::istream&) {} // A can be constructed from 'std::istream'.
};
struct B {
B(std::istream&) {} // B can be constructed from 'std::istream'.
};
int main() {
std::istringstream stream{"t1 t2"};
auto tup = parse<A, B>(stream);
}
它的工作原理是扩展可变参数类型列表,并使用提供的std::istream
作为参数构造每个类型。 然后将其留给每种类型的构造函数以从流中读取。
另请注意,未指定构造函数的求值顺序,因此您不能指望可变参数列表中的第一个类型将首先从流中读取。
代码本身不适用于int
, float
和char
内置类型,因为没有从std::istream
到任何这些类型的转换。
它的效果很差。 它依赖于具有带std::istream
的构造函数的目标类型。
由于许多类型没有这个,并且你不能将它添加到像int
这样的东西,这是一个糟糕的计划。
而是这样做:
template<class T>
auto read_from_stream( std::istream& stream, T* unused_type_tag )
-> typename std::decay<decltype( T{stream} )>::type
{
return {stream};
}
template<class T>
auto read_from_stream( std::istream& stream, T* unused_type_tag, ... )
-> typename std::decay<decltype( T(stream) )>::type
{
return T(stream);
}
template<typename... args>
std::tuple<args...> parse(std::istream& stream) {
return std::tuple<args...>{
read_from_stream(stream, (args*)nullptr)...
};
}
现在我们不是直接构造参数,而是调用read_from_stream
。
read_from_stream
上面有两个重载。 第一个尝试直接和隐式地从istream
构造我们的对象。 第二个显式从istream
构造我们的对象,然后使用RVO返回它。 ...
确保第二个仅在第一个失败时使用。
无论如何,这开辟了一个定制点。 在类型X
的命名空间中,我们可以编写一个read_from_stream( std::istream&, X* )
函数,它将自动被调用,而不是上面的默认实现。 我们还可以编写read_from_stream( std::istream&, int* )
(等),它可以知道如何解析istream
整数。
这种自定义点也可以使用traits类来完成,但是使用重载执行它有许多优点:您可以将自定义注入到类型旁边,而不必打开完全不同的命名空间。 自定义操作也更短(没有类包装噪音)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.