Когда необходимо определение статического члена класса (un / -) - PullRequest
0 голосов
/ 24 августа 2018

У меня большой проект, и я работаю над его рефакторингом. Основная задача - переписать логгер. Новый логгер (насколько я могу судить) API-совместим со старым, поэтому я полагал, что после изменения заголовка include directory, перекомпилировать и перекомпоновать все должно работать. Но нет. Я получаю несколько ошибок типа undefined reference to <static_data_member>. Я не могу вставить настоящий код, но он выглядит, например, так:

// Foo.h
class Foo {
    static const int bar = 0;
    int baz; // assigned in c-tor
    void updateBaz() { baz = bar; }
    // ....
}

static const int bar НЕ определено в Foo.cpp. Иногда это печатается с помощью макроса журнала. И раньше он работал (со старым логгером), теперь я должен его определить. Какие изменения могли быть причиной этого?

Другой пример того, что происходит с переменными, объявленными boost:

(...)/blog_adaptor.h:50: error: undefined reference to bbost::serialization::version<CA::CReyzinSignature>::value'

Итак: когда требуются определения для статических элементов и когда они могут быть опущены?

Ответы [ 2 ]

0 голосов
/ 27 августа 2018

Как ответил Николь Болас, код в моем проекте имел неопределенное поведение, поскольку члены статических данных были инициализированы, но нигде не определены. Подводя итог и расширить: Статический член данных не нужно определять, когда:

  • Он не используется или используется только в удаленных ветвях (неинстанцированные шаблоны и удаленные ветки constexpr-if)
  • в C ++ 17, если элемент встроенный
  • также clang-tidy говорит, что «внешнее определение члена статических данных constexpr избыточно в C ++ 17 и устарело», поэтому, вероятно, статическому constexpr это тоже не нужно

Далее, следующий код показывает, почему мой плохой проект раньше не вызывал ошибку компоновщика. Я не знаю, «не использует ли вы odr» или «Неопределенное поведение, которое вас пока не ранит»:

#include <boost/serialization/version.hpp>
class Klass {};
//BOOST_CLASS_VERSION(Klass, 3);
// would be expanded to:
namespace boost { namespace serialization {
template<>
struct version<Klass> {
    static const int value = 3; // not defined anywhere
};
} }

int foo (int val) { // was used by old logger
    return val;
}
int bar (const int &val) { // is used by new logger
    return val;
}
int main () {
//    return bar(boost::serialization::version<Klass>::value); // link error
    return foo(boost::serialization::version<Klass>::value); // works fine
}

Таким образом, нет ошибки связи, если используется элемент, но не запрашивается его адрес. Передача значения по ссылке квалифицируется как запрос адреса.

0 голосов
/ 24 августа 2018

Если переменные не объявлены inline (функция C ++ 17), определения статических переменных-членов не являются необязательными , что касается стандарта C ++. Непредоставление определения является неопределенным поведением.

Компиляторы и компоновщики могут различаться в зависимости от того, что заставит проверить их, чтобы увидеть, существуют ли определения, но это характер неопределенного поведения.

...