Статический полиморфизм: как определить интерфейс?

Ниже приведен очень простой пример того, что я понимаю как статический полиморфизм. Причина, по которой я не использую динамический полиморфизм, заключается в том, что я не хочу препятствовать встраиванию функций PROCESSOR в op.

template <class PROCESSOR>
void op(PROCESSOR* proc){
    proc->doSomething(5);
    proc->doSomethingElse();
}

int main() {
    ProcessorY py;
    op<ProcessorY>(&py);
    return 0;
}

Проблема с этим примером заключается в следующем: не существует явного определения того, какие функции должен определять PROCESSOR. Если один отсутствует, вы просто получите ошибку компиляции. Я думаю, что это плохой стиль.

У этого также есть очень практический недостаток: интерактивная поддержка IDE, конечно, не может показать вам функции, доступные для этого объекта.

Каков хороший/официальный способ определить публичный интерфейс PROCESSOR?


person Michael    schedule 26.04.2015    source источник
comment
Прежде всего, почему вы берете указатели? Во-вторых, определение дано вами в документации этого шаблона функции.   -  person Columbo    schedule 26.04.2015
comment
Кроме того, IDE, не показывающие вам список членов, не являются очень значительным недостатком.   -  person Columbo    schedule 26.04.2015
comment
Вам просто нужно дождаться Concepts TS.   -  person hynner    schedule 26.04.2015
comment
@Columbo: Указатели, потому что я не хочу передавать потенциально огромный ПРОЦЕССОР по значению. И мне лично нравятся предложения IDE, так что вопрос вкуса, значителен этот недостаток или нет...   -  person Michael    schedule 26.04.2015
comment
Почему указатели вместо ссылок?   -  person Columbo    schedule 26.04.2015
comment
@Columbo: В чем разница ссылок по этому вопросу?   -  person Michael    schedule 26.04.2015
comment
@Columbo Я предполагаю, что по причинам, описанным в этом соглашении, который я поддерживаю.   -  person Beta Carotin    schedule 26.04.2015
comment
@Майкл Нет. И я не подразумевал, что это делает один, также. Я был просто удивлен.   -  person Columbo    schedule 26.04.2015


Ответы (2)


Во-первых, я думаю, что с вашим примером статического полиморфизма проблем нет. Поскольку он статичен, т. е. разрешается во время компиляции, по определению он имеет менее строгие требования в отношении определения интерфейса.

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

Однако, если вы настаиваете на определении интерфейса, вы можете переписать свой пример следующим образом:

template <class Type>
class Processor
{
public:
    void doSomething(int);
    void doSomethingElse();
};

template <class Type>
void op(Processor<Type>* proc){
    proc->doSomething(5);
    proc->doSomethingElse();
}

// specialization
template <>
class Processor<Type_Y>
{
    // implement the specialized methods
};

typedef Processor<Type_Y> ProcessorY;

int main() {
    ProcessorY py;
    op(&py);
    return 0;
}
person valdo    schedule 26.04.2015
comment
@Puppy Пожалуйста, уточните, как это улучшит ответ. - person Beta Carotin; 26.04.2015
comment
Я думаю, что @valdo дал хороший ответ о возможном решении этой проблемы. Но я также был бы заинтересован в идее CRTP. - person Michael; 26.04.2015
comment
Предлагаемое Processor<T> решение ужасно. Это серьезно снижает способность op() быть универсальным, не обеспечивая при этом никаких преимуществ. Ваша специализация Processor<Type_Y> может так же легко не реализовать ни одну из этих функций, и тогда вы окажетесь точно в том же месте, с которого начали. - person Barry; 26.04.2015

Не существует явного определения того, какие методы должен определять ПРОЦЕССОР. Если один отсутствует, вы просто получите ошибку компиляции. Я думаю, это плохой стиль.

Это. Это не. Может быть. Это зависит.

Да, если вы хотите определить поведение таким образом, вы можете также ограничить содержимое, которое должны иметь параметры шаблона. К сожалению, сделать это «в явном виде» сейчас невозможно.

Вам нужна функция ограничений и концепций, которая должна была появиться как часть C++ 11, но был отложен и по-прежнему недоступен в C++ 14.

Однако получение ошибки времени компиляции часто является лучшим способом ограничить параметры шаблона. В качестве примера мы можем использовать библиотеку std:

1) Итераторы.

Библиотека C++ определяет несколько типов итераторов: forward_iterator, random_access_iterator и другие. Для каждого типа определен набор свойств и допустимых выражений, доступность которых гарантирована. Если вы использовали итератор, который не полностью совместим с random_access_iterator в контейнере, для которого требуется random_access_iterator, то в какой-то момент вы получите ошибку компилятора (скорее всего, при использовании оператора разыменования ([]), который требуется в этом классе итератора).

2) Распределители.

Все контейнеры в библиотеке std используют распределитель для выделения/освобождения памяти и построения объектов. По умолчанию используется std::allocator. Если вы хотите обменять его на свой, вам нужно убедиться, что в нем есть все, что гарантированно есть у std::allocator. В противном случае вы получите ошибку времени компиляции.

Итак, пока мы не получим концепции, это лучшее и наиболее широко используемое решение.

person Mateusz Grzejek    schedule 26.04.2015
comment
Проверки понятий можно легко эмулировать путем создания экземпляров ожидаемых указателей функций-членов проверяемого класса. - person πάντα ῥεῖ; 26.04.2015