Есть два способа сделать константное значение в классе:
Использовать перечисление:
class C {
enum { X = 42; }
};
Или статический член класса:
class C {
static const auto X = 42;
};
Какой способ предпочтительнее? Какие могут быть подводные камни?
Ответ
Статический член класса позволяет использовать вывод типов:
static const auto X = 1ul;
Для enum тип надо писать самому (если он не int):
enum { X = 1 };
enum : unsigned long { Y = 1 };
Получается что статический член класса выглядит предпочтительнее, к тому же он семантически является константой, а перечисление больше рассчитано на "перечисление" нескольких значений.
Подводные камни
Если мы попробуем скомпилировать следующий код
struct C {
static const auto X = 1;
};
int f(const int& arg) {
return arg;
}
int main() {
return f(C::X);
}
То мы можем получить ошибку линковки (при отключенном оптимизаторе):
main.cpp:(.text+0x15): undefined reference to `C::X'
Оказывается, статические целочисленные константные члены классов не требуют отдельного определения только если они не ODR-использованы (odr-used). Мы берем ссылку на C::X, это делает ее odr-used, и значит мы должны добавить ее определение вне класса:
struct C {
static const auto X = 1;
};
const int C::X; // В одном из .cpp файлов.
Термин "odr-used" определен в главе "One definition rule [basic.def.odr]" следующим образом:
Переменная, чье имя использовано в потенциально вычисляемом контексте (т.е. не sizeof или decltype), является odr-used, за исключением случая когда она является объектом, который может быть использован в константном выражении, и к ней сразу же применяется преобразование из lvalue в rvalue.
В нашем случае C::X может быть использована в константном выражении, однако она является lvalue и передается в f() как ссылка на lvalue. Если мы добавим преобразование в rvalue, то отдельное определение C::X уже будет не нужно, и ошибка линковки пропадет:
f(static_cast
Какой из этого можно сделать вывод?
Статические арифметические константные члены классов слишком удобны чтобы от них отказываться, и если вдруг появится ошибка линковки, то достаточно использовать унарный плюс чтобы ее починить.
Комментариев нет:
Отправить комментарий