Почему-то пребывал в уверенности, что если в 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 библиотеках.
Если бы все было так прекрасно, то не надо было inline variable вводить в C++ 20
ОтветитьУдалитьО прошу прощения это C++17
ОтветитьУдалитьЕсли Вы откроете пропосал 0386 для этой фичи то в самом конце найдете пункт Redaclaration of static constexpr data members там есть пример для c++14 где говорится о проблеме совместимости с раздельной декларацией и дифиницией и что это depricated in c++17, но допустимо.
ОтветитьУдалить