为什么C ++元组这么奇怪?

时间:2019-07-08 15:09:17

标签: c++ c++11 tuples c++-standard-library

在将不同类型的值分组在一起时,我通常会创建自定义structs。通常这很好,而且我个人觉得命名成员访问更容易阅读,但是我想创建一个更通用的API。在其他语言中广泛使用元组之后,我想返回std::tuple类型的值,但发现在C ++中使用它们比在其他语言中使用起来更加难看。

使元素访问使用get的整数模板参数的哪些工程决策如下?

#include <iostream>
#include <tuple>

using namespace std;

int main()
{
    auto t = make_tuple(1.0, "Two", 3);
    cout << "(" << get<0>(t) << ", " 
                << get<1>(t) << ", " 
                << get<2>(t) << ")\n";
}

不是像下面这样简单吗?

t.get(0)

get(t,0)

有什么好处?我只看到其中的问题:

  • 使用这样的template参数看起来很奇怪。我知道模板语言已经完成了Turing的功能,但仍然...
  • 这会使通过运行时生成的索引进行索引变得困难(例如,对于一个很小的有限范围索引,我已经看到了每种可能性使用switch语句编写的代码),或者如果范围太大,则无法进行索引。

编辑:我已经接受了答案。现在,我已经考虑了该语言需要了解的内容以及何时需要知道它,我认为这确实有意义。

4 个答案:

答案 0 :(得分:8)

您说的第二个:

  

它使通过运行时生成的索引进行索引变得困难(例如,对于一个很小的有限范围索引,我已经看到了每种可能性使用switch语句的代码),或者如果范围太大,则不可能。

C ++是一种 strong 静态类型的语言,必须确定所涉及的类型编译时

因此功能

template <typename ... Ts>
auto foo (std::tuple<Ts...> const & t, std::size_t index)
 { return get(t, index); }

不可接受,因为返回的类型取决于运行时值index

采用的解决方案:将索引值作为编译时间值,作为模板参数。

我想您知道,对于std::array,它是完全不同的:您有一个get()(方法at()operator[])接收运行时索引值:在std::array中,值类型与索引无关。

答案 1 :(得分:3)

std::get<N>中要求模板参数的“工程决策”的位置比您想象的要深。您正在查看静态动态类型系统之间的区别。我建议阅读https://en.wikipedia.org/wiki/Type_system,但这里有一些关键点:

  • 在静态类型中,必须在编译时知道变量/表达式的类型 。在这种情况下,无法使用get(int)的{​​{1}}方法,因为在编译时无法知道std::tuple<int, std::string>的参数。另一方面,由于必须在编译时知道模板参数,因此在这种情况下使用它们很有意义。

  • C ++确实具有多态类形式的动态类型。这些利用了运行时类型信息(RTTI),它会带来性能开销get的常规用例不需要动态输入,因此也不允许动态输入,但是C ++提供了用于这种情况的其他工具。
    举例来说,虽然您无法拥有同时包含std::tuplestd::vector的{​​{1}},但您完全可以拥有int,其中std::string包含std::vector<Widget*>IntWidget包含一个int,只要它们都源自StringWidget。假设,

    std::string

    您可以在向量的每个元素上调用Widget,而无需知道其确切的(动态)类型。

答案 2 :(得分:3)

  
      
  • 看起来很奇怪
  •   

这是一个很弱的论点。外表是一个主观问题。

函数参数列表根本不是编译时所需值的选项。

  
      
  • 这使通过运行时生成的索引建立索引变得困难
  •   

无论如何,运行时生成的索引都是困难的,因为C ++是一种静态类型的语言,没有运行时反映(甚至对此没有编译时反映)。考虑以下程序:

Django

std::tuple<std::vector<C>, int> tuple; int index = get_at_runtime(); WHATTYPEISTHIS var = get(tuple, index); 的返回类型应该是什么?您应该初始化哪种类型的变量?因为get(tuple, index)可能是1,所以它不能返回向量,由于index可能是0,它不能返回整数。在C ++中,所有变量的类型在编译时都是已知的。

当然,C ++ 17引入了index,在这种情况下这是一个潜在的选择。元组是C ++ 11中引入的,这不是一个选择。

如果需要对元组进行运行时索引,则可以编写自己的std::variant函数模板,该模板接受元组和运行时索引并返回get。但是使用变体并不像直接使用类型那样简单。这就是将运行时类型引入静态类型语言中的代价。

答案 3 :(得分:2)

请注意,在C ++ 17中,您可以使用structured binding使其更加明显:

SQL Error [0A000]: ERROR: EXECUTE of SELECT ... INTO is not implemented
  Hint: You might want to use EXECUTE ... INTO or EXECUTE CREATE TABLE ... AS instead.
  Where: PL/pgSQL function "JsonToView"(text) line 10 at EXECUTE