Я хотел написать несколько упаковщиков строк, которые будут принимать строку, если она допустима для их типа:
Length
допустимые строки: мм, м, фут, дюймAngle
допустимые строки: град, радЯ вообразил использование как:
Length len = read_from_keyboard(); // or some means of initialization
if( len.is_valid() ) { ... }
Итак, я написал эти реализации.
struct Length
{
QString m;
Length() {}
Length( QString s ) { if( is_valid_string(s) ) { m = s; } }
QString operator() () { return m; }
bool is_valid() { return is_valid_string(m); }
static bool is_valid_string( QString s ) {
return s == "mm" || s=="m" || s=="ft" || s=="in";
}
};
а также
struct Angle{
QString m;
Angle() {}
Angle( QString s ) { if( is_valid_string(s) ) { m = s; } }
QString operator() () { return m; }
bool is_valid() { return is_valid_string(m); }
static bool is_valid_string( QString s ) {
return s == "deg" || s=="rad";
}
};
Что мне кажется некой формой статического-полиморфизма, с is_valid_string()
как их единственная разница в реализации.
Поскольку у меня есть несколько этих классов, я подумал об использовании статического наследования (не через виртуальный) для понимания общей функциональности.
Итак, я подумал об использовании любопытно повторяющийся шаблон:
template <class T>
struct ConstrainedText {
QString m;
ConstrainedText() {}
ConstrainedText( QString s ) { if( T::is_valid_string(s) ) { m = s; } }
QString operator() () { return m; }
bool is_valid() { return T::is_valid_string(m); }
};struct Angle : public ConstrainedText<Angle> {
static bool is_valid_string( QString s ) {
return s == "deg" || s="rad";
}
};struct Length : public ConstrainedText<Angle> {
static bool is_valid_string( QString s ) {
return s == "mm" || s="m" || s=="ft" || s=="in";
}
};
но теперь я потерял неявные конструкторы внутри базового класса, и мне пришлось их переписать!
Есть ли какой-то другой способ, которым я мог бы реализовать это, чтобы иметь тот же интерфейс [default constructor
, implicit constructor
а также is_value()
] и пишите только минимальный код для другой части (статический is_valid_string()
)?
Я знаю, что мог бы использовать препроцессор, но я хочу, чтобы код был дружественным к отладчику.
Как уже отмечали другие, поскольку конструктор не наследуется, вам придется переопределить их. Вы могли бы сделать что-то вроде этого, код на ideone.com:
#include <string>
#include <stdexcept>
#include <iostream>
template <class T>
class ConstrainedText {
std::string m;
protected:
ConstrainedText() {}
~ConstrainedText() {}
public:
bool is_valid() {
return T::is_valid_string(m);
}
static T Create(std::string const & s)
{
if (T::is_valid_string(s)) {
T t;
static_cast<ConstrainedText<T>&>(t).m = s;
return t;
}
throw std::runtime_error("invalid input!");
}
};
struct Angle : public ConstrainedText<Angle> {
static bool is_valid_string( std::string s ) {
return s == "deg" || s=="rad";
}
};struct Length : public ConstrainedText<Length> {
static bool is_valid_string( std::string s ) {
return s == "mm" || s == "m" || s == "ft" || s == "in";
}
};
int main()
{
auto a = Angle::Create("deg");
auto l = Length::Create("mm");
try {
Angle::Create("bimbo");
} catch (std::runtime_error & pEx) {
std::cout << "exception as expected" << std::endl;
}
try {
Length::Create("bimbo");
} catch (std::runtime_error & pEx) {
std::cout << "exception as expected" << std::endl;
}
}
Конструкторы не наследуются, и вы не можете наследовать их с using
,
В C ++ 11 вы можете использовать шаблоны с переменными значениями и идеальную пересылку:
template<typename... Args> Derived(Args &&...args):
Base(std::forward<Args>(args)...) {}
Поскольку конструкторы никогда не наследуются в C ++ (кроме значения по умолчанию, если конструктор не определен), вы застряли либо в их самостоятельной реализации, либо в шаблонах (с или без шаблонов C ++ 11), чтобы компилятор соответствовал конструктору. Однако обратите внимание, что это усложняет интерпретацию ошибок документации и компилятора, поскольку бессмысленные конструкторы шаблонов добавляют (иногда) запутанный слой абстракции.
Операторы препроцессора также могут обойти это, но я бы сделал это, только если конструктор очень прост и легко реплицируется для всех наследников.
Таким образом, в общем случае ответ на вопрос, можете ли вы сгенерировать чистый унаследованный конструктор по умолчанию и автоматически неявный конструктор, — нет. Но два подхода, изложенные выше, сделали бы это несколько беспорядочно, но автоматически.
Я использовал политика и это спасло день. Теперь мне не нужно переписывать конструкторы, а мне нужно только реализовать is_valid_string()
, Теперь он также совместим с C ++ 98.
template <class Policy>
struct ConstrainedText {
QString m;
ConstrainedText() {}
ConstrainedText( QString s ) { if( Policy::is_valid_string(s) ) { m = s; } }
QString operator() () { return m; }
bool is_valid() { return Policy::is_valid_string(m); }
};struct Angle_policy {
static bool is_valid_string( QString s ) {
return s == "deg" || s="rad";
}
};
struct Length_policy {
static bool is_valid_string( QString s ) {
return s == "mm" || s="m" || s=="ft" || s=="in";
}
};
typedef ConstrainedText<Length_policy> Length;
typedef ConstrainedText<Angle_policy> Angle;
Спасибо за вашу помощь