在将不同类型的值分组在一起时,我通常会创建自定义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)
有什么好处?我只看到其中的问题:
编辑:我已经接受了答案。现在,我已经考虑了该语言需要了解的内容以及何时需要知道它,我认为这确实有意义。
答案 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::tuple
和std::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