Мы можем вызвать конструктор с использованием имени с квалифицированным именем, хотя конструктор не имеет имени. Действительно, 3.4.3.2/2:
В поиске, в котором имена функций не игнорируются, а спецификатор вложенных имен назначает класс C:
- если имя, указанное после вложенного имени-спецификатора, при поиске на языке С, представляет собой введенное имя класса C (пункт 9) или
[...]
вместо этого имя считается названным конструктором класса C.
Рассмотрим следующий пример:
#include <iostream>
using std::cout;
using std::endl;
struct A
{
virtual void foo()
{
cout << "A" << endl;
}
A(){ }
};
struct B : A
{
virtual void foo()
{
cout << "B" << endl;
}
B()
{
foo();
}
};
struct C : B
{
virtual void foo()
{
cout << "C" << endl;
}
C() : B(){ }
};
C c;
int main()
{
c.foo();
C::C(); // Prints B
}
Строка C::C()
печатает B. Но это неясно. В разделе 12.7/4 говорится:
Когда виртуальная функция вызывается прямо или косвенно из конструктора или из деструктора, в том числе при построении или уничтожении нестатических элементов данных классов, а объектом, к которому применяется вызов, является объект (назовите его x) под строительство или уничтожение, вызванная функция является окончательным перераспределением в классе конструкторов или деструкторов, а не одной, переопределяющей его в более производном классе
В явном конструкторе вызов c
уже полностью выполнен. Поэтому правило, которое я приводил, не может быть использовано для объяснения такого поведения. Это UB вообще? Не могли бы вы объяснить это?
Мы можем вызвать конструктор с использованием имени с квалифицированным именем, хотя конструктор не имеет имени.
Ваша предпосылка совершенно неверна. В том же абзаце, который вы указали (§3.4.3.1 [class.qual]/p2):
Такое имя конструктора должно использоваться только в идентификаторе-деклараторе объявления, которое называет конструктор или декларацию использования.
C::C();
объявление, которое называет конструктор? Нет. Это декларация использования? Очевидно нет.
Он плохо сформировался. По-видимому, Кланг считает, что вместо этого он назвал тип, по какой-то причине - вероятно, ошибкой (его обработка имен с введенными классами также является ошибкой в других отношениях).
Я также не знаю, как вы пришли к выводу, что поведение C::C();
- что, кстати, имеет смысл только в том случае, если вы считаете, что C::C
чтобы назвать тип, может быть затронуто состоянием c
, которое нигде не встречается в выражении или в какой-либо из соответствующих функций.
Гипотетический вызов явного конструктора объекта должен выглядеть как cC::C();
, потому что конструктор является нестатической функцией-членом. И не имеет никакого смысла разрешать вам вызывать конструктор на уже построенном объекте - что это значит?
C::C
должен называть тип, поскольку C
- это имя введенного класса , однако, как вы говорите, сам цитируемый текст говорит, что вместо этого считается, что он именует конструктор в этом контексте.
C::C()
очевидно, не имеет никакого смысла. Но Clang не выдавал ошибку времени компиляции, и я предполагаю, что это правильно. Я понял, что нет способа явно конструировать вызов. Спасибо.
Линия...
C::C();
выводит...
B
потому что foo()
вызывается изнутри конструктора B
поэтому он будет использовать B
версию foo()
.
Каждый раз, когда C
вызывается конструктор, C::C()
вызывает A
конструктор, который вызывает B
конструктор, в котором foo()
называется так B
версия foo()
используется, а затем C
тело конструктора (который вы оставили пустым), наконец, называется.
Пусть игнорирует тот факт, что вызов C::C()
из main()
не соответствует стандартам C++. Вы, кажется, неправильно интерпретируете часть раздела 12.7/4:
... вызванная функция является конечным переопределением в классе конструкторов или деструкторов, а не одной, переопределяющей его в более производном классе
Вызов foo находится в B
поэтому
... класс конструктора...
является B
и поэтому
... не один переопределяет его в более производном классе.
(который был бы C
).
Вы также указываете: "В явном вызове конструктора C
уже полностью сконструирован". Линия...
C c;
не изменит поведения, упомянутого в Разделе 12.7/4. foo()
все еще называется конструктором B
и поэтому
... вызванная функция является конечным переучителем в классе конструктора или деструктора.
C::C();
имеет отношение к объектуc
?