Статический const-доступ С++ через указатель NULL
class Foo {
public:
static const int kType = 42;
};
void Func() {
Foo *bar = NULL;
int x = bar->kType;
putc(x, stderr);
}
Это определенное поведение? Я прочитал стандарт С++, но не смог найти ничего о доступе к статическому значению const, как это... Я изучил сборку, подготовленную GCC 4.2, Clang++ и Visual Studio 2010, и ни один из них не выполняет разыменование NULL указатель, но я хотел бы быть уверен.
Ответы
Ответ 1
Вы можете использовать указатель (или другое выражение) для доступа к статическому члену; однако это делается с помощью указателя NULL, к сожалению, официально undefined поведение. Из 9.4/2 "Статические элементы":
Статический член s класса X может быть упоминается с использованием идентификатора с квалификацией выражение X:: s; это не обязательно использовать синтаксис доступа к члену класса (5.2.5) для обозначения статического члена. статический член может быть отнесен к использованию синтаксис доступа к члену класса, в в этом случае объект-выражение оценены.
На следующем примере:
class process {
public:
static void reschedule();
};
process& g();
void f()
{
process::reschedule(); // OK: no object necessary
g().reschedule(); // g() is called
}
Цель состоит в том, чтобы позволить вам гарантировать, что в этом сценарии будут вызываться функции.
Ответ 2
Я считаю, что фактическое значение типа вообще не используется при вызове
bar->kType
поскольку kType
является статическим, а bar имеет тип Foo
, это то же самое, что и вызов
Foo::kType
который вы действительно должны делать в любом случае для ясности.
Вызов bar->kType
дает предупреждение компилятора на большинстве платформ по этой причине.
Ответ 3
Помимо вопроса о доступе через указатель NULL, в коде есть еще одна тонкая проблема.
$9.4.2/2 - "Объявление статического члена данных в его определении класса не является определением и может иметь неполный тип, отличный от cv-qualoid void. Определение для статического элемента данных должно появиться в область пространства имен, охватывающая определение класса участников."
$9.4.2/4- "Если статический член данных имеет тип const const или const, его объявление в определении класса может указывать константный инициализатор, который должен быть интегральным постоянным выражением (5.19). В этом случае, член может отображаться в интегральных константных выражениях. Член все равно должен быть определен в области пространства имен, если он используется в программе, а определение области пространства имен не должно содержать инициализатор."
class Foo {
public:
static const int kType = 42;
};
int const Foo::kType;
void Func() {
Foo *bar = NULL;
int x = bar->kType;
putc(x, stderr);
}
Итак, еще одна причина для UB в OP-коде.
Ответ 4
Даже если это сработало, это ужасный код.
В серьезном программировании вы кодируете не только себя, но и других, которые будут поддерживать ваш код.
Подобные игры нужно избегать, потому что вы уважаете своих коллег.
Одно из последствий этого кода: является ли указатель NULL
или нет даже не под вопросом, но это означает, что этот член kType
может быть не простым нестационарным членом класса. Иногда классы большие (это тоже зло), и нельзя всегда перепроверять определение каждой переменной.
Будьте строги. И вызовите все ваши статические члены только следующим образом:
Foo::kType
Другая возможность заключается в том, чтобы следовать соглашению о кодировании, которое позволяет узнать, что элемент является статическим, например, префикс s_
для всех классов статических членов:
Foo::s_kType
Ответ 5
Существует более высокое правило, которое в принципе говорит - даже не думайте о компиляции вещей, которые, по-видимому, не используются. Расширенное программирование шаблонов сильно зависит от этого, поэтому даже если это может быть немного серо-зоническим, когда компилятор ясно видит, что результат конструкции не используется, он просто собирается его устранить. Особенно, когда это доказуемо безопасно, как в этом случае.
Вы можете попробовать несколько вариантов, если хотите - например, сделать указатель параметром функции, результатом функции, оставив указатель неинициализированным (наилучший шанс для запуска жалобы компилятора), делая прямой листинг из 0 (лучший шанс быть беззаботным).