【问题标题】:Tuple and variadic templates, how does this work?元组和可变参数模板,这是如何工作的?
【发布时间】:2015-01-22 05:51:40
【问题描述】:

我见过人们写(关于堆栈溢出本身,询问一些更高级的概念)类似的东西:

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

并将其用作

auto tup = parse<int, float, char>(stream);

上面的代码是如何通过解析流来构造元组的呢?对如何将数据放入流中是否有任何具体要求?

【问题讨论】:

标签: c++ c++11 tuples variadic-templates


【解决方案1】:

为此,必须进行从 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 作为参数构造每个类型。然后留给每种类型的构造函数从流中读取。

还要注意,构造函数的求值顺序没有指定,所以你不能指望可变参数列表中的第一个类型会首先从流中读取,等等。

代码原样intfloatchar等内置类型一起工作,因为没有从std::istream到任何这些类型的转换。

【讨论】:

  • 我在 VS2013 上,我收到以下错误错误 C2144: 语法错误:'A' 应该以 ')' 开头
【解决方案2】:

效果不佳。它依赖于目标类型,其构造函数采用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&amp;, X* ) 函数,它会自动被调用,而不是上面的默认实现。我们还可以编写read_from_stream( std::istream&amp;, int* ) (etc),它可以知道如何从istream 解析整数。

这种自定义点也可以使用特征类来完成,但使用重载进行有许多优点:您可以注入与类型相邻的自定义项,而不必打开完全不同的命名空间。自定义操作也更短(没有类包装噪音)。

【讨论】:

  • 这个实现有一个大问题,不能保证make_tuple 的参数是从左到右计算的。这是未指定的行为。事实上,clang 和 gcc 的行为恰恰相反。 GCC coliru.stacked-crooked.com/a/0688e941dfc9c563 和 CLANG coliru.stacked-crooked.com/a/d136ebd8dd32c43d
  • 您必须使用 std::tuple&lt;args...&gt; { blabla... } 来强制执行评估顺序,而不是使用 std::make_tuple( blabla... )(注意使用 {} 而不是 ())。
  • @galop1n 点头,已修复。顺便说一句,发现无法“直接通过函数构造”元组的各个部分很烦人:上述要求数据的移动能力,没有充分的理由。
  • @Yakk,我正在使用 vs2013 来使用您的代码,它会哭泣:错误 C2668:'read_from_stream':对重载函数的模糊调用。有什么想法吗?
  • @arun 消除第一个过载,第二个消除...。无论如何,它只在极端情况下有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 1970-01-01
  • 2017-07-11
相关资源
最近更新 更多