- PVSM.RU - https://www.pvsm.ru -
Прочитав статью «Вычислите длину окружности» [1], которая, в общем-то, крайне позабавила меня своим стилем, и узнав для себя кое-что новое, я стал несколько сомневаться в достаточной подробности предложенной информации. Всё-таки компиляторов довольно много, систем тоже немало, а в статье как-то навеяно Windows и Visual Studio (на правах ИМХО).
Речь пойдёт о примерах после вынесения «загадочной» константы за пределы функции. Так как правда об этом действе хуже рассказанной хитрым профессором. Примечание: я тестил всё исключительно «на имеющихся под рукой»:
Не исключаю чего-то ещё более неординарного под другими системами. Буду рад, если кто-то расскажет о них мне.
Учёный студент, закончив институт и придя на работу разработчиком C++ (скажем, под основные десктопные системы, Linix, OS X, Windows), узнаёт, что в компании все уже перешли на C++11 совместимые компиляторы. Обрадованный возможностью писать проще и короче, герой в одном из заголовочных файлов пишет так:
constexpr char sin_tables[4096] { /* Заполните значениями, если очень хочется */};
Примерно через час после коммита из соседнего отдела, который тестит сборки под OS X, раздаётся недоумевающий крик, что бинарнику сильно поплохело. Всё просто, в отличие от Visual C++ (определённых новых версий), ни clang (3.5, 3.6), ни gcc (4.8, 4.9) не полагаются на подобное неустановленное поведение (компилятор Microsoft отчего-то перестал видеть в constexpr обычную переменную с внутренней линковкой и сделал, впрочем, доброе дело), и мы получили дублирование нашего массива.
Пример:
a.cpp
#include <cstdio>
#include "h.h"
void printSin1() {
for (auto &e : sin_tables)
printf("%fn", e);
}
int main() {
printSin1();
printSin2();
}
b.cpp
#include <cstdio>
#include "h.h"
void printSin2() {
for (auto &e : sin_tables)
printf("%fn", e);
}
h.h
constexpr float sin_tables[4096] { /* ... */};
void printSin1();
void printSin2();
Для тех, кому лень собирать:

Если вы считаете, что виновата OS X, то спешу вас уверить, эта «фича» присутствует и в Ubuntu, и в MinGW. Это вполне рациональная особенность компилятора…
Вот же напасть, посчитал наш юный программист. Может, написать хак для этих пингвино-маководов. Сказано — сделано.
#if !defined(CONST_UNIQUE)
#if defined(MSVC)
#define CONST_UNIQUE constexpr
#else
#define CONST_UNIQUE extern __attribute__((weak)) constexpr
#endif
#endif
Всё работает, всё собирается. Но через пару минут из той же комнаты доносятся новые вопли: нерабочий коммит, билдбот выкинул ошибку, что же ты делаешь?! В проблеме опять же нет ничего необычного. Clang, хоть и делающий попытки реализовать всё нестандартное в gcc, полностью совместим никогда не был и вряд ли будет. Ну и потому в особых случаях использования этого атрибута нас будет ждать сюрприз (ну как же без этого).
CONST_UNIQUE int A = 137; // gcc OK, clang OK
CONST_UNIQUE int B = A+1; // gcc OK, clang OK
#include <array>
std::array<int, A> loveClang; // gcc OK, clang FAIL
./file.cpp:21:17: error: non-type template argument is not a constant expression
std::array<int, A> loveClang;
^
Так-то всё правильно, как этот человек вообще посмел написать хак, да и отправить его в транк. История умалчивает, что в особо «понимающих» случаях в MinGW может быть применён хак selectany, вместо weak, как более Windows-подобный. Однако от вышесказанной ошибки он тоже не спасёт.
Как показывает мой личный опыт, магические константы обычно специфичны для какого-то блока или модуля программы. Засорять ими глобальное пространство имён требуется достаточно редко, а потому сейчас никто вам не запретит написать:
MyClass.h
class MyClass {
/* ... */
static constexpr float m_pi {3.14};
/* ... */
};
MyClass.cpp
constexpr float MyClass::m_pi;
Что это вполне решит вопрос с читабельностью констант, с их видимостью и запретным размножением. С линковкой, да, вопрос остаётся открытым, но это больше, чем ничего (особенно с учётом стандартности реализации). Чтобы избежать обвинений плагиата, добавляю ссылку сюда [2], где подобное также описано.
Впрочем, вам не кажется, что вышесказанное — это серьёзное зацикливание на очень небольшой проблеме? В реальности все и так понимают:
Автор: vit9696
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/86972
Ссылки в тексте:
[1] «Вычислите длину окружности»: http://habrahabr.ru/company/abbyy/blog/252871
[2] сюда: http://stackoverflow.com/questions/14872240/unique-address-for-constexpr-variable
[3] Источник: http://habrahabr.ru/post/254077/
Нажмите здесь для печати.