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

Инициализация переменных-членов с тем же именем для аргументов конструктора, что и для переменных-членов, разрешенных стандартом С++?

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

#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++. Интересно, что об этом говорит стандарт С++? Является ли законным и гарантированным всегда работать?

4b9b3361

Ответ 1

Интересно, что об этом говорит стандарт С++? Является ли законным и гарантированным всегда работать?

Да. Это совершенно законно. Полностью стандартная совместимость.

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

Поскольку вы попросили ссылку в стандарте, вот он, с примером.

§12.6.2/7

Имена в списке выражений mem-инициализатора оцениваются в области конструктора, для которого указан mem-инициализатор.

[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:: я со значением конструктора параметр я и инициализирует X:: j с значение X:: i; это происходит каждый раз, когда объект класса X создано. ]

[Примечание: поскольку mem-initializer оцениваются в объем конструктора, этот указатель может использоваться в выражение-список mem-инициализатора для ссылки на объект, являющийся инициализируется. ]

Как вы можете видеть, в приведенном выше примере есть и другая интересная вещь, и комментарий к самому стандарту.


Кстати, в качестве побочного примечания, почему вы не принимаете параметр в качестве ссылки на const:

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

Это позволяет избежать ненужной копии исходного векторного объекта.

Ответ 2

Гарантируется всегда работать (я использую его довольно часто). Компилятор знает, что список инициализаторов имеет форму: member(value), и поэтому он знает, что первый vec в vec(vec) должен быть членом. Теперь в аргументе инициализации члена могут использоваться оба члена, аргументы конструктору и другим символам, как и в любом выражении, которое будет присутствовать внутри конструктора. В этот момент он применяет регулярные правила поиска, а аргумент vec скрывает член vec.

В разделе 12.6.2 стандарта рассматривается инициализация, и в нем объясняется процесс с параграфом 2, касающийся поиска элемента и параграфа 7 с поиском аргумента.

Имена в списке выражений mem-инициализатора оцениваются в области конструктора, для которого указан mem-инициализатор. [Пример:

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) {}
};

Ответ 3

Как уже ответили другие: Да, это законно. И да, это гарантируется стандартом для работы.

И я нахожу это ужасным каждый раз, когда вижу это, заставляя меня делать паузу: " vec(vec)? WTF? Ах да, vec - переменная-член..."

Это одна из причин, почему многие, в том числе и я, любят использовать соглашение об именах, которое дает понять, что переменная-член является переменной-членом. Соглашения, которые я видел, включают добавление суффикса подчеркивания (vec_) или префикса m_ (m_vec). Затем инициализатор читает: vec_(vec)/m_vec(vec), что не m_vec(vec).

Ответ 4

Еще один контраргумент или, возможно, просто кое-что, о чем следует знать, - это ситуация, в которой конструкция перемещения используется для инициализации переменной-члена.

Если переменная-член должна использоваться в теле конструктора, то на переменную-член нужно ссылаться прямо через указатель this, в противном случае будет использоваться перемещенная переменная, которая находится в неопределенном состоянии.

template<typename B>
class A {
public:

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

private:
  B b;
};