Я знаю, что это в основном обходной путь, который вы нашли и не любите, но я хотел задокументировать его, а также сказать, что это в основном текущее решение этой проблемы.
Я долго искал способ сделать это и не нашел хорошего решения.
Тот факт, что это невозможно, является причиной того, что в конечном итоге такие вещи, как boost::iterator_facade<Self, different_type, value_type, ...>
, нуждаются во многих параметрах.
Конечно, мы бы хотели, чтобы что-то вроде этого работало:
template<class CRTP>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using ptr_type = typename CRTP::value_type*; // doesn't work, A is incomplete
};
template<class T>
struct A : incrementable<A<T>>{
void increment(){}
using value_type = T;
value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}
Если бы это было возможно, все черты производного класса могли бы передаваться неявно от базового класса. Идиома, которую я нашел, чтобы получить тот же эффект, состоит в том, чтобы передать черты полностью базовому классу.
template<class CRTP, class ValueType>
struct incrementable{
void operator++(){static_cast<CRTP&>(*this).increment();}
using value_type = ValueType;
using ptr_type = value_type*;
};
template<class T>
struct A : incrementable<A<T>, T>{
void increment(){}
typename A::value_type f() const{return typename A::value_type{};}
// using value_type = typename A::value_type;
// value_type f() const{return value_type{};}
};
int main(){A<double> a; ++a;}
https://godbolt.org/z/2G4w7d
Недостатком является то, что к черте в производном классе нужно обращаться с квалифицированным typename
или , повторно включаемым на using
.