Почему вызывает статическую функцию-член. или → синтаксис законный?
Возможный дубликат:
Вызов метода статического члена С++ в экземпляре класса
Сегодня я обнаружил, что что-то, что у меня было давно (и я имею в виду давно, например, двадцать лет), считалось незаконным в С++, действительно легально. А именно, вызов статической функции-члена, как если бы она принадлежала отдельному объекту. Например:
struct Foo
{
static void bar() { cout << "Whatever."; }
};
void caller()
{
Foo foo;
foo.bar(); // Legal -- what?
}
Обычно я вижу, что статические функции-члены называются строго с "синтаксисом разрешения разрешения", таким образом:
Foo::bar();
Это имеет смысл, потому что статическая функция-член не связана с каким-либо конкретным экземпляром класса, поэтому мы не ожидаем, что конкретный экземпляр будет синтаксически "прикреплен" к вызову функции.
Однако я обнаружил, что GCC 4.2, GCC 4.7.1 и Clang 3.1 (как случайная выборка компиляторов) принимают прежний синтаксис, а также:
Foo* foo = new Foo;
foo->bar();
В моем конкретном случае законность этого выражения привела к ошибке времени выполнения, которая убедила меня в том, что особенность этого синтаксиса имеет больший академический интерес - это имеет практические последствия.
Почему С++ разрешает статические функции-члены вызываться так, как если бы они были непосредственными членами отдельных объектов, то есть с помощью. или → , прикрепленный к экземпляру объекта?
Ответы
Ответ 1
Предположительно, вы можете вызвать его в местах, где вы, возможно, не знаете тип класса, но компилятор делает.
Скажем, у меня была группа классов, каждая из которых имеет статический член, который возвращает имя класса:
class Foo
{
static const char* ClassName() { return "Foo"; }
};
class Bar
{
static const char* ClassName() { return "Bar"; }
};
Затем по всему моему коду я мог делать такие вещи, как:
Foo foo;
printf( "This is a %s\n", foo.ClassName() );
Не нужно беспокоиться о знании класса моих объектов все время. Это было бы очень удобно при написании шаблонов, например.
Ответ 2
В дизайне и эволюции С++ на стр. 288 Bjarne Stroustrup упоминает, что за несколько дней до статических функций-членов программисты использовали хаки типа ((X*)0)->f()
для вызова функций-членов, которым не нужен объект. Я предполагаю, что когда статические функции-члены были добавлены к языку, доступ через ->
был разрешен, так что программисты с таким кодом могли бы изменить f
на static
без необходимости выслеживать и изменять каждое его использование.
Ответ 3
Это так, потому что стандарт говорит, что он работает. n3290 § 9.4:
Статический член класса X может ссылаться на использование идентификатора с квалификацией выражение X::s;
нет необходимости использовать доступ к члену класса синтаксис (5.2.5) для ссылки на статический член. Статический член может быть ссылается на использование синтаксиса доступа к члену класса, и в этом случае выражение объекта оценивается. [Пример:
struct process {
static void reschedule();
};
process& g();
void f() {
process::reschedule(); // OK: no object necessary
g().reschedule(); // g() is called
}
конец примера]
Ответ 4
Из Эволюция С++ (pdf), раздел 8. Статические функции участника:
... Было также отмечено, что непереносимый код, например
((x*)0)->f();
использовался для моделирования статических функций-членов.
Итак, моя догадка (основанная на шаблоне логики почти для любой другой странной синтаксической вещи), они позволили вызвать статическую функцию-член, когда у вас только был тип, обеспечивающий обратную совместимость с установленной, но сломанной идиомой.
Ответ 5
Если вы не подписываетесь на "потому что стандарт говорит так" школу причинности, я также предлагаю, чтобы статические методы были достаточно старыми, чтобы происходить с того времени, когда люди действительно беспокоились о дополнительных накладных расходах от прохождения this
аргумент для вызова функции, поэтому чистые функции "статические" в качестве оптимизации, вероятно, были в ярости в 1985 году.