вторник, 3 декабря 2019 г.

[prog.c++] Похоже, что втоптался в неприятную особенность static constexpr членов класса

Почему-то пребывал в уверенности, что если в C++ объявлять статические члены класса как static constexpr, то компилятор с линкером автоматически будут разруливать ситуации с множественными определениями таких статических членов в разных единицах трансляции. Но не так все просто оказалось.

Дабы не быть голословным создал просто демо-пример.

Тут есть заголовочный файл qvalue.hpp, внутри которого объявляется класс со static constexpr полем maximum.

Есть два файла (a.cpp и b.cpp), которые внутри себя загружают qvalue.hpp и используют qvalue_t::maximum. Эти файлы компилируются по отдельности и результаты их компиляции затем собираются в один статический файл.

Так вот, если компилировать более-менее современными версиями GCC и clang (скажем, начиная от GCC-5.5 и clang-6.0) с оптимизацией, т.е.:

$ g++ -std=c++14 a.cpp b.cpp main.cpp -O2

то никаких проблем нет, все успешно компилируется и линкуется.

Но вот если собирать без оптимизации, т.е.:

$ g++ -std=c++14 a.cpp b.cpp main.cpp -O0

то возникает ошибка линковки:

/tmp/ccHv44S3.o: In function `a(unsigned int&)':
a.cpp:(.text+0x1e): undefined reference to `qvalue_t::maximum'
/tmp/ccIQACTW.o: In function `b(unsigned int&)':
b.cpp:(.text+0x1e): undefined reference to `qvalue_t::maximum'
collect2: error: ld returned 1 exit status

При этом в VC++ проблем нет.

Нахожусь сейчас в состоянии разорванного напрочь шаблона. Ибо это получается, что static constexpr поля, которые должны инициализироваться прямо в определении класса, оказываются неприменимы в header-only библиотеках.

3 комментария:

DLoginov комментирует...

Если бы все было так прекрасно, то не надо было inline variable вводить в C++ 20

DLoginov комментирует...

О прошу прощения это C++17

DLoginov комментирует...

Если Вы откроете пропосал 0386 для этой фичи то в самом конце найдете пункт Redaclaration of static constexpr data members там есть пример для c++14 где говорится о проблеме совместимости с раздельной декларацией и дифиницией и что это depricated in c++17, но допустимо.