Инициализация переменных-членов с тем же именем для аргументов конструктора, что и для переменных-членов, разрешенных стандартом С++?
Я понял, что можно инициализировать переменные-члены с аргументом конструктора с тем же именем, что и 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++. Интересно, что об этом говорит стандарт С++? Является ли законным и гарантированным всегда работать?
Ответы
Ответ 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;
};