Я заметил странное поведение при попытке скомпилировать приведенный ниже код. У меня есть 4 файла следующим образомОшибка компилятора или правильное поведение для статической константной переменной-члена, вариативных шаблонов и &&?
createshared.h:
#ifndef CREATESHARED_H_
#define CREATESHARED_H_
#include <memory>
#include <utility>
#ifdef USE_REFREF
template<typename T, typename... Args>
std::shared_ptr<T> create_shared(Args&&... args)
{
class HelperClass : public T
{
public:
HelperClass (Args&& ... nargs) : T(std::forward<Args...>(nargs)...) {}
virtual ~HelperClass() = default;
};
return std::make_shared<HelperClass>(std::forward<Args...>(args)...);
}
#else
template<typename T, typename... Args>
std::shared_ptr<T> create_shared(Args... args)
{
class HelperClass : public T
{
public:
HelperClass (Args ... nargs) : T(nargs...) {}
virtual ~HelperClass() = default;
};
return std::make_shared<HelperClass>(args...);
}
#endif
#endif
staticinitclass.h
#ifndef STATICINITCLASS_H_
#define STATICINITCLASS_H_
class StaticInitClass
{
public:
#ifdef INITIALIZE_IN_HEADER
static const int default_i = 1;
#else
static const int default_i;
#endif
virtual ~StaticInitClass() = default;
StaticInitClass() = delete;
protected:
StaticInitClass(int i);
};
#endif
staticinitclass.cpp:
#include "staticinitclass.h"
#include <iostream>
#ifndef INITIALIZE_IN_HEADER
const int StaticInitClass::default_i = 2;
#endif
StaticInitClass::StaticInitClass(int i)
{
std::cout << "Created with " << i << std::endl;
}
main.cpp:
#include "staticinitclass.h"
#include "createshared.h"
#include <memory>
int main(int argc, const char* argv[])
{
auto shared = create_shared<StaticInitClass>(StaticInitClass::default_i);
}
Без флагов программа компилируется и работает нормально.
$ g++ -std=c++11 main.cpp staticinitclass.cpp
$ ./a.out
Created with 2
Fine, потому что default_i является целочисленный тип, мы можем инициализировать его в заголовке. Давайте сделаем это
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DINITIALIZE_IN_HEADER
$ ./a.out
Created with 1
Хорошо, по-прежнему собирает и работает нормально. Теперь, давайте добавим наш & & и зЬй :: вперед
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DINITIALIZE_IN_HEADER -DUSE_REFREF
/tmp/cc3G4tjc.o: In function `main':
main.cpp:(.text+0xaf): undefined reference to `StaticInitClass::default_i'
collect2: error: ld returned 1 exit status
Linker ошибку. Ну, давайте попробуем инициализацию нашего default_i члена в .cpp
$ g++ -std=c++11 main.cpp staticinitclass.cpp -DUSE_REFREF
$ ./a.out
Created with 2
И снова работает. Использование clang дает тот же результат, который заставил бы меня поверить, что это не просто изолированная ошибка компилятора, а, возможно, что-то в самом языке, который предотвращает статическую инициализацию. Я просто не могу объяснить, почему добавление & & приведет к перерыву.
В настоящее время я использую г ++ 4.8.2 и лязг ++ 3.5 на Ubuntu 14.04
Любые идеи, что сломана здесь при использовании -DINITIALIZE_IN_HEADER и -DUSE_REFREF?
Предоставление инициализатора для статического элемента данных не означает, что вы также предоставили определение. Когда вы объединяете две опции '-D', вы в конечном итоге делаете odr-использование этого статического члена и, следовательно, должны предоставить определение для него. – Praetorian