Доступ к оператору [] из указателя

#c #vector #refactoring

#c #указатели #операторы

Вопрос:

Если я определяю указатель на объект, который определяет [] оператор, есть ли прямой способ получить доступ к этому оператору из указателя?

Например, в следующем коде я могу напрямую обращаться Vec к функциям-членам (таким как empty() ), используя оператор указателя -> , но если я хочу получить доступ к [] оператору, мне нужно сначала получить ссылку на объект, а затем вызвать оператор.

 #include <vector>

int main(int argc, char *argv[])
{
    std::vector<int> Vec(1,1);
    std::vector<int>* VecPtr = amp;Vec;

if(!VecPtr->empty())      // this is fine
    return (*VecPtr)[0]; // is there some sort of ->[] operator I could use?

return 0;
}
  

Я вполне могу ошибаться, но похоже, что выполнение (*VecPtr).empty() менее эффективно, чем выполнение VecPtr->empty() . Именно поэтому я искал альтернативу (*VecPtr)[] .

Комментарии:

1. Почему вы хотите избежать (*VecPtr)[0] ?

2. @Rob Отредактирован с указанием причины. Мое предположение может быть неверным, но именно поэтому я спросил.

3. (*VecPtr).empty() и VecPtr->empty() являются одним и тем же (если только VecPtr не является чем-то, что перегружает operator* и operator-> имеет противоречивое значение.

4. Если VecPtr является типом указателя, то (*VecPtr).XXX , по определению, эквивалентно VecPtr->XXX . Потери эффективности нет. If VecPtr — это тип без указателя, который реализует operator* и operator[] , тогда вам нужно будет изучить их, чтобы увидеть, какой из них более эффективен (вероятно, они одинаковы).

5. Как этот вопрос получает одобрение?? Он основан на одной из самых плохо информированных и недостаточно изученных предпосылок и вряд ли представляет ценность для других.

Ответ №1:

Вы можете выполнить любое из следующих действий:

 #include <vector>

int main () {
  std::vector<int> v(1,1);
  std::vector<int>* p = amp;v;

  p->operator[](0);
  (*p)[0];
  p[0][0];
}
  

Кстати, в конкретном случае std::vector , вы также можете выбрать: p->at(0) , хотя это имеет немного другое значение.

Комментарии:

1. Последний интересный, не могли бы вы объяснить, что там происходит под капотом? Это первый [0], ссылающийся на «первый элемент», на который указывает указатель? Аналогично тому, как векторная ссылка является указателем на начало массива?

2. @ThekoLekena точно, для указателей на полные типы T оператор [] определен для доступа к i-му элементу массива (путем вычисления sizeof(T) * i). Конечно, поскольку в этом случае у нас есть указатель на один вектор, наличие чего-либо еще, кроме [0], было бы неопределенным поведением. редактировать : я бы не стал выбирать последний, поскольку я нахожу его менее идиоматичным.

3. @ancientchild согласился, последний не следует использовать в качестве обычной практики.

Ответ №2:

 return VecPtr->operator[](0);
  

…сделает свое дело. Но на самом деле (*VecPtr)[0] форма выглядит лучше, не так ли?

Ответ №3:

(*VecPtr)[0] все в порядке, но вы можете использовать at функцию, если хотите:

 VecPtr->at(0);
  

Имейте в виду, что это (в отличие operator[] от) вызовет std::out_of_range исключение, если индекс не находится в диапазоне.

Ответ №4:

Есть другой способ, вы можете использовать ссылку на объект:

 #include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v = {7};
    vector<int> *p = amp;v;

    // Reference to the vector
    vector<int> amp;r = *p;
    cout << (*p)[0] << 'n'; // Prints 7
    cout <<    r[0] << 'n'; // Prints 7

    return 0;
}
  

Этот способ r такой же, как v и, и вы можете заменить все вхождения (*p) на r .

Предостережение: это будет работать только в том случае, если вы не будете изменять указатель (т. Е. Изменять, На какой объект он указывает).

Рассмотрим следующее:

 #include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector<int> v = {7};
    vector<int> *p = amp;v;

    // Reference to the vector
    vector<int> amp;r = *p;
    cout << (*p)[0] << 'n'; // Prints 7
    cout <<    r[0] << 'n'; // Prints 7

    // Caveat: When you change p, r is still the old *p (i.e. v)
    vector<int> u = {3};
    p = amp;u; // Doesn't change who r references
    //r = u; // Wrong, see below why
    cout << (*p)[0] << 'n'; // Prints 3
    cout <<    r[0] << 'n'; // Prints 7

    return 0;
}
  

r = u; неверно, потому что вы не можете изменять ссылки:
Это изменит вектор, на который ссылается r ( v )
, вместо ссылки на другой вектор ( u ) .
Итак, опять же, это работает только в том случае, если указатель не изменится при использовании ссылки.

Примеры нуждаются в C 11 только из-за vector<int> ... = {...};

Комментарии:

1. r = u; это нормально, и он будет выполнять присваивание между двумя векторами; это не имеет ничего общего с p

2. И это то, что я подразумеваю под «неправильным». Цитирую себя: «вы можете заменить все вхождения (*p) на r «, но «Это будет работать, только если вы не будете изменять указатель», потому что нет способа изменить r , как вы изменили бы значение *p . r = u; «изменит вектор, на который ссылается, r вместо ссылки на другой вектор» — это не будет иметь желаемого эффекта.

3. r = u; будет иметь тот же эффект, независимо от того, измените вы позже указатель или нет

4. Да, но я не понимаю, почему вы это говорите. Опять же, под «неправильным» я подразумеваю, что он не делает то, что p = ... мог бы сделать. И я говорю о замене всех *p случаев на by r . Но если в вашем коде у вас есть p = ... , замена p = ... на r = ... является неправильной.

Ответ №5:

Вы можете использовать его как VecPrt->operator [] ( 0 ) , но я не уверен, что он покажется вам менее непонятным.

Ответ №6:

Стоит отметить, что в C 11 std::vector имеет функцию-член ‘data’, которая возвращает указатель на базовый массив (как const, так и неконстантные версии), что позволяет вам написать следующее:

 VecPtr->data()[0];
  

Это может быть альтернативой

 VecPtr->at(0);
  

что влечет за собой небольшие накладные расходы во время выполнения, но, что более важно, его использование подразумевает, что вы не проверяете индекс на достоверность перед его вызовом, что неверно в вашем конкретном примере.

Для получения более подробной информации см. std::vector::data .

Ответ №7:

Люди советуют вам использовать ->at(0) из-за проверки диапазона. Но вот мой совет (с другой точки зрения):

НИКОГДА не используйте ->at(0) ! Это действительно медленнее. Вы бы пожертвовали производительностью только потому, что вы достаточно ленивы, чтобы не проверять диапазон самостоятельно? Если это так, вам не следует программировать на C .

Я думаю (*VecPtr)[0] , все в порядке.

Комментарии:

1. почему at(0) работает медленно?