Вызов статического метода путем повторения имени объекта
У меня есть singleton:
struct foo {
static foo& instance() {
static foo f;
return f;
}
};
При повторной компоновке кода я закончил с этим утверждением "по ошибке":
foo::foo::instance()
Но мой компилятор (gcc 4.7) считается правильным. На самом деле даже foo::foo::foo::instance()
компилируется. Почему?
Ответы
Ответ 1
Это происходит из-за "injected-name" — что означает, что foo
- это имя класса, и в класс-область также вводится одно и то же имя "foo", поэтому ваш код работает. Он соответствует стандарту 100%.
Вот один интересный пример, который показывает преимущества этой функции:
namespace N
{
//define a class here
struct A
{
void f() { std::cout << "N::A" << std::endl; }
};
}
namespace M
{
//define another class with same name!
struct A
{
void f() { std::cout << "M::A" << std::endl; }
};
struct B : N::A //NOTE : deriving from N::A
{
B()
{
A a;
a.f(); //what should it print?
}
};
}
Что нужно a.f()
позвонить? Каков тип a
? Это M::A
или N::A
? Ответ: N::A
, not M::A
.
Это связано с именем-инъекцией, N::A
доступно внутри конструктора B
без квалификации. Он также скрывает M::A
, который остается вне области B
. Если вы хотите использовать M::A
, тогда вы должны написать M::A
(или лучше ::M::A
).
Ответ 2
Из-за [class]/2
:
Имя класса вставляется в область, в которой объявляется сразу после просмотра имени класса. Имя класса также вставляется в область самого класса; это известно как имя введенного класса.
So foo::foo
- это введенное имя класса, обозначающее foo
.
На самом деле это немного сложнее: согласно [class.qual]/2
, foo::foo
сам по себе обозначает конструктор foo
. Чтобы обозначить класс, ему должно предшествовать struct
(что делает его уточненным-спецификатором типа) или следует за ::
(что делает его вложенным именем-спецификатором - это ваш случай), или базовый-спецификатор (например, struct bar : foo::foo {};
).
Ответ 3
Как указано в других ответах, причиной является инъекция имени. Для меня основным вариантом использования было бы следующее
struct B1 { void f(){} };
struct B2 { void f(){} };
struct D : B1, B2 { }
int main() {
D obj;
obj.f();
}
В main
вызов f
неоднозначен и не будет компилироваться. Способ быть конкретным - это квалифицированный вызов, т.е.
obj.B1::f();