Я создаю тип контейнера и пытаюсь повторно использовать как можно больше кода, используя Удивительно повторяющийся шаблон шаблона.
Вот базовый код, который в некоторых случаях компилируется, а в других нет:
template<typename T, size_t N, typename ConcreteClass> struct _BaseClassOperators {
const ConcreteClass& operator+=(const ConcreteClass& other) {
ConcreteClass& self = static_cast<ConcreteClass&>(*this);
for(size_t i = 0; i < N; ++i) {
self.data[i] += other.data[i];
}
return self;
}
const ConcreteClass& operator+=(T value) {
ConcreteClass& self = static_cast<ConcreteClass&>(*this);
for(size_t i = 0; i < N; ++i) {
self.data[i] += value;
}
return self;
}
friend ConcreteClass operator+(ConcreteClass lhs, const ConcreteClass& rhs) {
lhs += rhs;
return lhs;
}
friend ConcreteClass operator+(ConcreteClass lhs, T value) {
lhs += value;
return lhs;
}
friend ConcreteClass operator+(T value, ConcreteClass rhs) {
rhs += value;
return rhs;
}
};
template<typename T, size_t N, typename ConcreteClass> struct _BaseClass : public _BaseClassOperators<T, N, ConcreteClass> {
};
template<typename T, typename ConcreteClass> struct _BaseClass<T, 3, ConcreteClass> : public _BaseClassOperators<T, 3, ConcreteClass> {
constexpr _BaseClass() : data { 0, 0, 0 } {}
constexpr _BaseClass(T first, T second, T third) : data { first, second, third } {}
explicit constexpr _BaseClass(T value) : data { value, value, value } {}
static constexpr ConcreteClass SomeInstance = ConcreteClass(ConcreteClass::SomeConstant, ConcreteClass::SomeConstant, ConcreteClass::SomeConstant);
T data[3];
};
template<typename T, size_t N> struct DerivedClass : public _BaseClass<T, N, DerivedClass<T, N>> {
using _BaseClass<T, N, DerivedClass>::_BaseClass;
static constexpr T SomeConstant = 0;
};
template<> struct DerivedClass<float, 3> : public _BaseClass<float, 3, DerivedClass<float, 3>> {
using _BaseClass::_BaseClass;
static constexpr float SomeConstant = 5.0f;
};
typedef DerivedClass<float, 3> DC3;
typedef DerivedClass<uint8_t, 3> DCI3;
Когда я использовал оператор +
, взяв два экземпляра DC3
, все скомпилировалось без проблем:
DC3 do_something() {
return DC3::SomeInstance + DC3::SomeInstance;
}
int main(int argc, char* argv[]) {
do_something();
return 0;
}
Когда я изменил do_something
на вызов оператора +
с DC3
и float
, он жалуется:
DC3 do_something() {
return DC3::SomeInstance + 0.5f;
}
line number of do_something()...
: неопределенная ссылка на `_BaseClass >::SomeInstance'
Почему в данном случае SomeInstance
не определено?
Я также получаю ту же ошибку, если определяю перегруженный operator[]
в классе операторов и использую его для реализации operator+=(const DerivedClass& other)
, изменяя self.data[i]
на self[i]
.
Я также заметил, что если я определяю SomeInstance
как const
вместо constexpr
и инициализирую его (по шаблону) сразу после тела шаблона, у меня не возникает никаких проблем.
В случае, если это ошибка, я использую avr-g++ (gcc) 4.8.1 на OSX с определенным -std=gnu++11
.
constexpr
константу в производном классе. Даже если вместо этого я использую пустой конструкторDerivedClass()
и избегаю ссылок на константы, у меня все еще есть проблема. Это тоже незаконно? - person Merlyn Morgan-Graham   schedule 31.07.2016DC3::SomeInstance
, поэтому ему нужно определение (тот, что в определении класса, - это просто объявление перед С++ 17, подробности и стандартные кавычки в одном из вопросов, которые вы связали). Добавлениеtemplate<class T, class C> constexpr C _BaseClass<T, 3, C>::SomeInstance;
в пространство имен приведет к компиляции кода. Обратите внимание, чтоconstexpr
в этом определении на самом деле должно бытьconst
, но GCC ошибочно отклоняет это< /а>. - person bogdan   schedule 31.07.2016ConcreteClass
относится к классу, определение которого еще не видели когда создается экземпляр_BaseClass<float, 3, DerivedClass<float, 3>>
. - person bogdan   schedule 31.07.2016