Как работает эта перегрузка квадратных скобок

Я хочу избежать печати пробела ("") или символа подчеркивания ("_") после последнего элемента массива при печати их с использованием цикла for. В задаче упоминается один из способов сделать это, я хочу понять, как и почему это происходит.

    vector<int> v1 = {1, 2, 3, 4, 5};
    int n = v1.size();
    for (int i = 0; i < n; i++) {
        cout << v1[i] << "_"[i==n-1];
    }

Это печатает 1_2_3_4_5, как и ожидалось.

Хочу обойти работу этого оператора. Как он работает и какие другие аспекты он охватывает.

Это просто реализация оператора индекса над вектором (даже без перегрузки). И вектор должен выделять непрерывный кусок памяти. Так что это просто вопрос вычисления смещения в памяти и возврата найденного там элемента. Возможно, это вам поможет learncpp.com/cpp-tutorial/overloading-the-subscript-operator

Pepijn Kramer 17.02.2023 09:59

Я рекомендую вам инвестировать в несколько хороших книг по C++ для начинающих, так как они объяснят все это (и многое другое).

Some programmer dude 17.02.2023 09:59

Распечатайте "_"[0] и "_"[1]. Что вы получаете?

PaulMcKenzie 17.02.2023 10:02

Я не уверен, что это хорошо печатать \0, может быть, так и должно быть "_" + (i==n-1), но все же не рекомендую.

apple apple 17.02.2023 10:08

То, что это выводит, зависит от того, на какой носитель он выводится - некоторые терминалы отображают нулевой символ как «ничего», но это не гарантируется.

molbdnilo 17.02.2023 10:21

В перегрузке нет никакой магии. Логическое значение можно преобразовать в целое число; false на 0, а true на 1. Так что это то же самое, что и int index = i==n-1 ? 1 : 0; cout << v1[i] << "_"[index];

molbdnilo 17.02.2023 10:24

Обратитесь к , как спрашивать , где первым шагом является «поиск, а затем исследование», и вы найдете множество обманов для этого.

Jason Liam 17.02.2023 10:52

@PaulMcKenzie, ваш пример был в точку, спасибо.

Faizan Ahmed 17.02.2023 12:57
Laravel с Turbo JS
Laravel с Turbo JS
Turbo - это библиотека JavaScript для упрощения создания быстрых и высокоинтерактивных веб-приложений. Она работает с помощью техники под названием...
Типы ввода HTML: Лучшие практики и советы
Типы ввода HTML: Лучшие практики и советы
HTML, или HyperText Markup Language , является стандартным языком разметки, используемым для создания веб-страниц. Типы ввода HTML - это различные...
Аутсорсинг разработки PHP для индивидуальных веб-решений
Аутсорсинг разработки PHP для индивидуальных веб-решений
Услуги PHP-разработки могут быть экономически эффективным решением для компаний, которые ищут высококачественные услуги веб-разработки по доступным...
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Слишком много useState? Давайте useReducer!
Слишком много useState? Давайте useReducer!
Современный фронтенд похож на старую добрую веб-разработку, но с одной загвоздкой: страница в браузере так же сложна, как и бэкенд.
Узнайте, как использовать теги &lt;ul&gt; и &lt;li&gt; для создания неупорядоченных списков в HTML
Узнайте, как использовать теги <ul> и <li> для создания неупорядоченных списков в HTML
HTML предоставляет множество тегов для структурирования и организации содержимого веб-страницы. Одним из наиболее часто используемых тегов для...
2
8
77
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

В этом заявлении

cout << v1[i] << "_"[i==n-1];

Выражение "_"[i==n-1] является выражением со строковым литералом "_".

Строковые литералы имеют типы константных массивов символов. Литерал "_" имеет тип const char[2] и представляется в памяти как

{ '_', '\0' }

Таким образом, выражение "_"[i==n-1] использует оператор нижнего индекса со строковым литералом, который является массивом. Выражение i == n-1 всегда равно false, которое преобразуется в целое число 0, когда i не равно n-1. В противном случае он равен true, который преобразуется в 1.

Таким образом, у вас есть либо "_"[0], что дает символ '_', либо "_"[1], который дает символ '\0';.

То есть, когда i не равно n-1 у вас на самом деле есть

cout << v1[i] << '_';

И когда i равно n -1, у вас действительно есть

cout << v1[i] << '\0';

Обратите внимание, что в этом выражении "_"[i==n-1] строковый литерал неявно преобразуется в указатель на его первый элемент.

В качестве альтернативы вы можете написать с тем же эффектом

vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
const char literal[] = "_";
for (int i = 0; i < n; i++) {
    cout << v1[i] << literal[i==n-1];
}

Чтобы расширить свои знания, имейте в виду, что это выражение "_"[i==n-1] вы также можете переписать как ( i == n-1 )["_"], хотя такое выражение только запутает читателей кода.

Из стандарта С++ 17 (8.2.1 Подписка)

1 Постфиксное выражение, за которым следует выражение в квадратных скобках, постфиксное выражение. Одно из выражений должно быть glvalue введите «массив T» или prvalue типа «указатель на T», а другой должно быть значением перечисления с незаданной областью или целочисленного типа. результат имеет тип «Т». Тип «Т» должен быть полностью определенным тип объекта.66 Выражение E1[E2] идентично (по определению) выражению *((E1)+(E2)) [Примечание: см. 8.3 и 8.7 для подробностей * и + и 11.3.4 для деталей массивов. — примечание в конце] , за исключением случаев, когда операнд массива, результатом является lvalue, если этот операнд является lvalue и xvalue в противном случае. Выражение E1 расположено перед выражение Е2.

Обратите внимание на то, что этот фрагмент кода

vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
for (int i = 0; i < n; i++) {
    cout << v1[i] << "_"[i==n-1];
}

Может выглядеть более ясно, используя цикл for на основе диапазона. Например

std::vector<int> v1 = {1, 2, 3, 4, 5};

bool next = false;
for ( const auto &item : v1 ) 
{
    if ( next ) std::cout << '_';
    else next = true; 

    std::cout << item;
}

Если компилятор поддерживает стандарт С++ 20, вы можете написать также как

std::vector<int> v1 = {1, 2, 3, 4, 5};

for ( bool next = false; const auto &item : v1 ) 
{
    if ( next ) std::cout << '_';
    else next = true; 

    std::cout << item;
}

Спасибо за подробное объяснение, это развеяло сомнения.

Faizan Ahmed 17.02.2023 12:56

@FaizanAhmed Вовсе нет. Пожалуйста.:)

Vlad from Moscow 17.02.2023 13:09

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

#include <vector>
#include <iostream>
    
// using namespace std; ehr no don't do this
  
int main()
{
    std::vector<int> v1{ 1, 2, 3, 4, 5 };
   
    for (std::size_t i{ 0ul }; i < v1.size(); ++i)
    {
        if (i != 0ul) std::cout << "_";
        std::cout << v1[i];
    }
    std::cout << "\n";
   
    // with vector use range based for if you can
    // and then it becomes
    bool print_underscore = false;
    
    for (const int value : v1)
    {
        if (print_underscore) std::cout << "_";
        std::cout << value;
        print_underscore = true;
    }
    std::cout << "\n";
    
    return 0;
}
Ответ принят как подходящий

С [] здесь ничего особенного не происходит. Это обычный доступ к элементам. "_" — это const char [2], где первый символ — _, а второй — нулевой терминатор \0.

Вы можете сделать то же самое со строкой:

vector<int> v1 = {1, 2, 3, 4, 5};
int n = v1.size();
for (int i = 0; i < n; i++) {
    cout << v1[i] << std::string{"_"}[i==n-1];
}

Обратите внимание, что std::string является исключением среди контейнеров, и можно читать \0 из str[str.size()].

\0 не является печатным символом, поэтому вы видите вывод, который видите.

Возможно, код станет более понятным, если вы явно используете пустую строку:

vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<std::string> d{ "_",""};
int n = v1.size();
for (int i = 0; i < n; i++) {
    cout << v1[i] << d[i==n-1];
}

В качестве альтернативы, поскольку вы все равно используете цикл на основе индекса (а не цикл на основе диапазона, который следует предпочесть), вы можете начать цикл со второго элемента:

std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<std::string> d{ "_",""};
int n = v1.size();
if (n) std::cout << v1[0];
for (int i = 1; i < n; i++) {
    std::cout << "_" << v1[i];
}

""_" это char [2]..." Нет, это не так

Jason Liam 17.02.2023 10:53

apple apple 17.02.2023 15:38

Другие вопросы по теме