Ініціалізація змінних-членів з використанням того самого імені для аргументів конструктора, що і для змінних-членів, дозволених стандартом C ++?


76

Я з’ясував, що можна ініціалізувати змінні-члени аргументом конструктора з тим самим іменем, як показано в прикладі нижче.

#include <cstdio>
#include <vector>

class Blah {
    std::vector<int> vec;

public:
    Blah(std::vector<int> vec): vec(vec)
    {}

    void printVec() {

        for(unsigned int i=0; i<vec.size(); i++)
            printf("%i ", vec.at(i));

        printf("\n");
    }
};

int main() {

    std::vector<int> myVector(3);

    myVector.at(0) = 1;
    myVector.at(1) = 2;
    myVector.at(2) = 3;

    Blah blah(myVector);

    blah.printVec();

    return 0;
}

g ++ 4.4 з аргументами -Wall -Wextra -pedanticне дає жодного попередження і працює коректно. Він також працює з clang ++. Цікаво, що про це говорить стандарт С ++? Чи законно та гарантовано завжди працювати?


1
Гарне питання. Я насправді досить часто використовую цей "стиль". Ніколи не сумнівався, що це дозволено.
ltjax

Відповіді:


92

Цікаво, що про це говорить стандарт С ++? Чи законно та гарантовано завжди працювати?

Так. Це цілком законно. Повністю стандартний відповідник.

Blah(std::vector<int> vec): vec(vec){}
                             ^   ^                           
                             |   |
                             |    this is the argument to the constructor
                             this is your member data

Оскільки ви попросили посилання у Стандарті, ось воно, з прикладом.

§ 12.6.2 / 7

Імена у списку виразів мем-ініціалізатора обчислюються в межах конструктора, для якого вказано мем-ініціалізатор.

[Example:
class X {
 int a;
 int b;
 int i;
 int j;
 public:
 const int& r;
  X(int i): r(a), b(i), i(i), j(this->i) {}
                      //^^^^ note this (added by Nawaz)
};

ініціалізує X :: r для посилання на X :: a, ініціалізує X :: b зі значенням параметра конструктора i, ініціалізує X :: i зі значенням параметра конструктора i та ініціалізує X :: j зі значенням з X :: i; це відбувається кожного разу, коли створюється об'єкт класу X. ]

[Примітка: оскільки мем-ініціалізатор обчислюється у межах конструктора, цей покажчик може бути використаний у списку виразів мем-ініціалізатора для посилання на об’єкт, який ініціалізується. ]

Як бачите, у наведеному вище прикладі та коментарі самого стандарту є ще щось цікаве.


До речі, як додаткове зауваження, чому ви не приймаєте параметр як посилання на const :

 Blah(const std::vector<int> & vec): vec(vec) {}
      ^^^^const              ^reference

Це дозволяє уникнути непотрібної копії оригінального векторного об'єкта.


Thx за швидку відповідь, однак у документі стандартів (n3290) я не можу знайти списки ініціалізації, який розділ?
Нільс,

3
У C ++ 0x , що краще прийняти його за значенням, а потім перемістити його за призначенням: Blah(std::vector<int> vec) : vec(std::move(vec)){}. Ви можете змоделювати в C ++ 03 , як це: Blah(std::vector<int> vecSrc) { std::swap(vec, vecSrc); }.
GManNickG

3
@Tomalak: ха-ха, я все ще здивований, що ти вважаєш це нечитабельним. Це досить прямо і, я думав, загальне місце.
GManNickG

1
@GMan: Я все ще вважаю це "трюком". Я знаю, що це робить, але з коду з першого погляду не видно, що це означає , я думаю. Вам потрібно продумати ці кроки, щоб усвідомити, що оригінальний оригінальний об’єкт нікуди не переміщується.
Гонки легкості на орбіті

2
Хоча це законно, це погана практика. Він забруднює воду, використовуючи -Wshadow (або еквівалент), і відкриває вам більш згубні помилки програмування.
Рік,

15

Це гарантовано працює завжди (я користуюся цим досить часто). Компілятор знає, що список ініціалізатора має форму:, member(value)і тому він знає, що перший vecв vec(vec)повинен бути членом. Тепер для аргументу для ініціалізації члена можуть використовуватися обидва члени, аргументи конструктора та інші символи, як у будь-якому виразі, який міститься всередині конструктора. На даний момент він застосовує звичайні правила пошуку, а аргумент vecприховує учасникаvec .

Розділ 12.6.2 стандарту стосується ініціалізації, і він пояснює процес з параграфом 2, що стосується пошуку для члена, а в пункті 7 - з пошуку аргументу.

Імена у списку виразів мем-ініціалізатора обчислюються в межах конструктора, для якого вказано мем-ініціалізатор. [Приклад:

class X {
   int a;
   int b;
   int i;
   int j;
public:
   const int& r;
   X(int i): r(a), b(i), i(i), j(this->i) {}
};

2
+1 для ключової точки, яку всі інші залишили невисловленою: це працює, оскільки застосовуються ті самі правила пошуку, що і в тілі ctor, що - і це ключова точка - означає, що аргумент приховує / затінює учасника, саме тому це працює (для певного визначення слова "робота"; будь-яке приховування / переслідування є поганим стилем IMHO).
underscore_d

2

Одним з додаткових аргументів зустрічного або, можливо, просто чогось, про що слід знати, є ситуація, коли конструкція переміщення використовується для ініціалізації змінної-члена.

Якщо змінну-член потрібно використовувати в тілі конструктора, тоді на змінну-член потрібно чітко посилатися через цей вказівник, інакше буде використана переміщена змінна, яка знаходиться у невизначеному стані.

template<typename B>
class A {
public:

  A(B&& b): b(std::forward(b)) {
    this->b.test(); // Correct
    b.test(); // Undefined behavior
  }

private:
  B b;
};

1

Як вже відповіли інші: Так, це законно. І так, це гарантується стандартом для роботи.

І мені це стає жахливо кожного разу, коли я це бачу, змушуючи мене робити паузу: " vec(vec)? WTF? Ах так,vec це змінна члена ..."

Це одна з причин, чому багато хто, в тому числі і я, люблять використовувати конвенцію про іменування, яка дає зрозуміти, що змінна-член є змінною-членом. Конвенції, які я бачив, включають додавання суфікса підкреслення ( vec_) або m_префікса ( m_vec). Потім ініціалізатор читає: vec_(vec)/ m_vec(vec), що не вимагає особливих зусиль.


Простіше виявити помилку в: x (y), y (y), ніж виявити ту саму помилку в: m_x (in_y), m_y (in_y). Найменш оздоблені імена менше відволікають увагу і дозволяють читачеві зосередитись лише на суттєвому, і в підсумку це зрештою має менше шансів на помилки. Ці правила в стандарті були обрані не випадково, вони мають своє призначення.
Dom
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.