Подтвердить что ты не робот

Почему мы не можем обращаться к элементам кортежа по индексу?

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

2 работает. 1 не работает.

Почему это так?

Из Lounge С++ я узнал, что это, вероятно, потому, что компилятор не знает, какой тип данных хранится в этом индексе. Но для меня это не имело большого смысла, поскольку компилятор мог просто найти декларацию этого кортежа и определить тип данных или сделать что-то еще, получая доступ к элементам других структур данных по индексу.

4b9b3361

Ответ 1

Потому что [] является оператором (с именем operator[]), поэтому является функцией-членом и вызывается во время выполнения.

В то время как получение элемента кортежа является механизмом шаблона, его необходимо разрешить во время компиляции. Это означает, что это можно сделать только с синтаксисом < > templating.

Чтобы лучше понять, кортеж может хранить разные типы. Функция шаблона может возвращать разные типы в зависимости от переданного индекса, поскольку это разрешено во время компиляции. Оператор [] должен возвращать уникальный тип, независимо от значения переданного параметра. Таким образом, функциональность кортежа не достижима.

get<0>(x) и get<1>(x) - две разные функции, сгенерированные во время компиляции, и возвращают разные типы. Компилятор генерирует фактически две функции, которые будут искажены чем-то вроде

int get_tuple_int_string_int_0(x)

и

string get_tuple_int_string_int_1(x)

Ответ 2

В других ответах здесь рассматривается вопрос о том, почему это невозможно реализовать, но также стоит задать вопрос о том, возможно ли это. (Ответ - нет.)

Подстрочный оператор [] семантически предполагается указывать доступ динамически разрешен к элементу коллекции, например массив или список (любой реализации). Шаблон доступа обычно подразумевает определенные вещи: количество элементов, вероятно, неизвестно окружающему коду, к которому элемент доступа обращается, вероятно, будет изменяться во время выполнения, и все элементы имеют один и тот же наблюдаемый тип (таким образом, к вызывающему коду, взаимозаменяемые).

Вещь есть, кортеж не является (такого рода) коллекцией. Это фактически анонимный struct, и его элементы не являются взаимозаменяемыми слотами вообще - семантически, они являются регулярными полями. Вероятно, вы выбрасываете вас из-за того, что они помечены цифрами, но это действительно просто анонимный шаблон именования - аналогичный доступу к элементам как x._0, x._1 и т.д. (Факт, что вы можете вычислить имена полей в compile-time - это совпадающий бонус, который активируется системой типа С++ и не имеет принципиальной связи с кортежем: кортежи и этот ответ не очень специфичны для С++.)

Поэтому он не поддерживает operator[] по той же причине, что простые старые структуры не поддерживают operator[]: в этом контексте семантически допустимое использование для него не существует. Структуры имеют фиксированный набор полей, которые не являются взаимозаменяемыми или динамически вычисляемыми, и поскольку кортеж представляет собой структуру, а не коллекцию, он следует тому же правилу. Его имена полей выглядят иначе.

Ответ 3

Это не очень чистая поддержка operator[], поскольку вы не можете изменять статический тип возврата для соответствия доступному элементу. Если стандартная библиотека включила что-то вроде boost::any или boost::variant, это имело бы смысл.

Другими словами, если вы пишете что-то вроде:

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

Тогда что делать, если n не обращается к int члену tuple? Чтобы даже поддерживать проверку, вам нужно сохранить некоторую информацию о типе RunTime Type для tuple s, что является дополнительным издержком в исполняемом файле/памяти.

Ответ 4

Он может быть поддержан, ему просто нужно взять индекс времени компиляции. Поскольку параметры функции не могут быть сделаны constexpr, нам нужно обернуть индекс внутри типа и передать это. (например, std::integral_constant<std::size_t, N>.

Ниже приведено расширение std::tuple, которое поддерживает 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 и переменные шаблоны, возможно, не существовали в то время, когда был введен std::tuple. Что касается того, почему этого не существует, хотя эти функции существуют, я бы догадался, потому что никто еще не предложил его.

Ответ 5

Поскольку кортеж не имеет оператора "скобка".
Почему это так? Вы не можете разрешать шаблоны, основанные только на возвращаемом значении. Вы не можете писать

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

Что абсолютно необходимо, чтобы иметь возможность разрешать такие выражения, как x[0]