为什么我们不能通过索引访问元组的元素?

时间:2015-09-16 10:47:42

标签: c++ stl

tuple <int, string, int> x=make_tuple(1, "anukul", 100);
cout << x[0];       //1
cout << get<0>(x);  //2

2件作品。 1没有。

为什么会这样?

来自Lounge C ++我了解到这可能是因为编译器不知道该索引存储的数据类型。 但它对我来说没有多大意义,因为编译器可以只查找该元组的声明并确定数据类型,或者在通过索引访问其他数据结构元素时执行其他任何操作。

6 个答案:

答案 0 :(得分:31)

因为[]是一个运算符(名为>>> d datetime.datetime(2015, 9, 15, 17, 13, 29, 380000) >>> d.timestamp() 1442330009.38 >>> import pytz >>> d.replace(tzinfo=pytz.timezone("US/Eastern")).timestamp() 1442355209.38 ),因此是一个成员函数,并在运行时被调用。

虽然获取元组项是模板机制,但必须在编译时解析它。这意味着只能通过&lt;&gt;来完成模板语法。

为了更好地理解,元组可以存储不同的类型。模板函数可能会返回不同的类型,具体取决于传递的索引,因为这是在编译时解决的。 无论传递的参数的值是什么,operator []必须返回唯一类型。因此,元组功能无法实现。

operator[]get<0>(x)是在编译时生成的两个不同的函数,并返回不同的类型。编译器实际上会生成两个函数,这些函数将被修改为类似

的函数
get<1>(x)

int get_tuple_int_string_int_0(x)

答案 1 :(得分:12)

这里的其他答案解决了为什么无法实现这一问题的问题,但也值得问一下是否应该的问题。 (答案是否定的。)

下标运算符[]在语义上应该表示对集合元素的动态解析访问,例如数组或列表(任何实现)。访问模式通常意味着某些事情:周围代码可能不知道元素的数量,正在访问的元素可能在运行时变化,并且元素都是相同的可观察类型(因此,对于调用代码) ,可互换)。

事情是,元组不是(那种)集合。它实际上是一个匿名的struct,它的元素根本不是可互换的插槽 - 从语义上讲,它们是常规字段。可能会让你失望的是它们碰巧被标记为数字,但这实际上只是一种匿名命名模式 - 类似于以x._0x._1等方式访问元素。(你可以计算的事实)字典名称​​在编译时是由C ++的类型系统启用的巧合奖励,并且与元组的基本没有关系;元组和这个答案并不是真正特定于C ++。)< / p>

所以它不支持operator[],原因与普通旧结构不支持operator[]的原因相同:在此上下文中没有语义有效的用法。结构具有一组固定的字段,这些字段不可互换或可动态计算,并且由于元组结构而不是集合,因此它遵循相同的规则。它的字段名称看起来不同。

答案 2 :(得分:5)

支持operator[]不是很干净,因为你不能改变静态返回类型以匹配被访问的元素。如果标准库包含boost::anyboost::variant之类的内容,则更有意义。

换句话说,如果你写下这样的话:

int n = atoi(argv[1]);
int x = x[n];

如果n未解决int的{​​{1}}成员,该怎么办?为了支持检查,您需要为tuple存储某种方式的RunTime类型信息,这是可执行文件/内存中的额外开销。

答案 3 :(得分:4)

可以支持它,它只需要一个编译时索引。由于函数的参数不能成为constexpr,我们需要将索引包装在一个类型中并传递它。 (例如std::integral_constant<std::size_t, N>

以下是支持std::tuple的{​​{1}}扩展程序。

operator[]

它会像这样使用:

template <typename... Ts>
class tuple : public std::tuple<Ts...> {
  public:
  using std::tuple<Ts...>::tuple;

  template <std::size_t N>
  decltype(auto) operator[](std::integral_constant<std::size_t, N>) {
    return std::get<N>(*this);
  }
};

为了缓解tuple<int, std::string> x(42, "hello"); std::cout << x[std::integral_constant<std::size_t, 0>{}] << std::endl; // prints: 42 疯狂,我们可以使用变量模板:

std::integral_constant

有了这个,我们可以说:

template <std::size_t N>
std::integral_constant<std::size_t, N> ic;

所以可以做到。关于为什么目前不可用的一个猜测是因为std::cout << x[ic<1>] << std::endl; // prints: hello 和变量模板等功能可能在std::integral_constant被引入时不存在。至于为什么即使存在这些特征也不存在,我猜它是因为还没有人提出它。

答案 4 :(得分:1)

因为元组没有操作符“bracket” 为什么会这样?您无法仅根据返回值解析模板。你不能写

template<typename T>
T tuple::operator [](size_t i) const ;

能够允许x[0]

等语句是绝对必要的

答案 5 :(得分:1)

支持下标运算符(即operator[])(例如std::vectorstd::array)的容器是 齐次 。无论提供给下标运算符的索引是什么,要返回的值始终是同一类型。因此,这些容器可以使用以下声明来定义成员函数:

T& operator[](int);

T集合中每个元素的类型

另一方面,std::tupe 异构 的集合。 std::tuple的假定下标运算符的返回值需要随索引而变化。因此,其返回类型取决于索引。

在上面给出的operator[]的声明中,索引作为函数参数提供,因此可以在运行时确定。但是,函数的返回类型是需要在编译时确定的,而不是在运行时确定的。

由于此类函数的返回类型取决于索引,但必须在编译时确定,因此解决方案是改为定义一个将索引作为(非类型)模板参数。这样,索引作为编译时常量提供,并且返回类型可以随索引而改变:

template<std::size_t I, class... Types>
typename std::tuple_element<I, tuple<Types...>>::type& get(tuple<Types...>&) noexcept;

如您所见,std::get的返回类型取决于索引I

std::tuple_element<I, tuple<Types...>>::type&