Рефакторинг на шаблоны

Я хотел написать несколько упаковщиков строк, которые будут принимать строку, если она допустима для их типа:

  • 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() )?

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

1

Решение

Как уже отмечали другие, поскольку конструктор не наследуется, вам придется переопределить их. Вы могли бы сделать что-то вроде этого, код на 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;
}
}
1

Другие решения

Конструкторы не наследуются, и вы не можете наследовать их с using,

В C ++ 11 вы можете использовать шаблоны с переменными значениями и идеальную пересылку:

template<typename... Args> Derived(Args &&...args):
Base(std::forward<Args>(args)...) {}
1

Поскольку конструкторы никогда не наследуются в C ++ (кроме значения по умолчанию, если конструктор не определен), вы застряли либо в их самостоятельной реализации, либо в шаблонах (с или без шаблонов C ++ 11), чтобы компилятор соответствовал конструктору. Однако обратите внимание, что это усложняет интерпретацию ошибок документации и компилятора, поскольку бессмысленные конструкторы шаблонов добавляют (иногда) запутанный слой абстракции.

Операторы препроцессора также могут обойти это, но я бы сделал это, только если конструктор очень прост и легко реплицируется для всех наследников.

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

0

Я использовал политика и это спасло день. Теперь мне не нужно переписывать конструкторы, а мне нужно только реализовать 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;

Спасибо за вашу помощь

0
По вопросам рекламы [email protected]