C ++ имеет некоторую типизацию утилит для типов, заданных параметрами шаблона. Мы понятия не имеем, какого типа DUCK1
а также DUCK2
будет, но пока они могут quack()
, он скомпилирует и запустит:
template <class DUCK1, class DUCK2>
void let_them_quack(DUCK1* donald, DUCK2* daisy){
donald->quack();
daisy->quack();
}
Но это немного неудобно писать. Когда мне абсолютно все равно, какие актуальные типы DUCK1
а также DUCK2
но я хочу в полной мере использовать идею утиной печати, тогда я хотел бы иметь что-то немного отличающееся от описанного выше:
Предлагает ли C ++ какие-либо функции для реализации одной или нескольких из трех идей?
(Я знаю, что виртуальное наследование является методом выбора в большинстве случаев для реализации таких шаблонов, но вопрос здесь конкретно касается случая статического полиморфизма.)
По вопросам 1 и 2: начиная с C ++ 14 вы можете опустить явное template <typename ...
шаблон и использование auto
, но только в лямбдах:
auto let_them_quack = [] (auto & donald, auto & daisy){
donald.quack();
daisy.quack();
};
(да, я предпочитаю ссылки на указатели). GCC позволяет делать это в обычных функциях как расширение.
На вопрос 3, о чем вы говорите, называются концепции. Они существовали в C ++ долгое время, но только как документальный термин. Теперь Концепции ТС в процессе, что позволяет вам написать что-то вроде
template<typename T>
concept bool Quackable = requires(T a) {
a.quack();
};
void let_them_quack (Quackable & donald, Quackable & daisy);
Обратите внимание, что это еще не C ++, а только техническая спецификация. GCC 6.1, похоже, уже поддерживает это. Возможны реализации концепций и ограничений с использованием текущего C ++; Вы можете найти один в увеличение.
Я хотел бы пропустить написание списка параметров шаблона, который повторяется
и в основном бессмысленно (только представьте, что будет, если будет 7
утки …)
Для этого вы можете использовать шаблоны variadic и сделать что-то вроде следующего:
template<typename DUCK>
void let_them_quack(DUCK &&d) {
d.quack();
}
template<typename DUCK, typename... Args>
void let_them_quack(DUCK &&d, Args&& ...args) {
d.quack();
let_them_quack(std::forward<Args>(args)...);
}
# 2 и # 3 в некотором роде заботятся о том, что код не будет компилироваться, и выдает ошибку компиляции, если данные классы не реализуют интерфейс. Вы также можете сделать это формальным:
class duck {
public:
virtual void quack()=0;
};
Затем объявите параметры функции как указатель на утку. Ваши классы должны будут унаследовать от этого класса, делая требования для let_them_quack()
кристально чистый.
Что касается # 1, вариационные шаблоны могут позаботиться об этом.
void let_them_quack()
{
}
template <typename ...Args>
void let_them_quack(duck* first_duck, Args && ...args) {
first_duck->quack();
let_them_quack(std::forward<Args>(args)...);
}
Вы сможете сделать его более привлекательным с концепцией (еще не в стандарте — но очень близко):
http://melpon.org/wandbox/permlink/Vjy2U6BPbsTuSK3u
#include <iostream>
template<typename T>concept bool ItQuacks(){
return requires (T a) {
{ a.quack() } -> void;
};
}
void let_them_quack2(ItQuacks* donald, ItQuacks* daisy){
donald->quack();
daisy->quack();
}
struct DisneyDuck {
void quack(){ std::cout << "Quack!";}
};
struct RegularDuck {
void quack(){ std::cout << "Quack2!";}
};
struct Wolf {
void woof(){ std::cout << "Woof!";}
};
int main() {
DisneyDuck q1, q2;
let_them_quack2(&q1, &q2);
RegularDuck q3, q4;
let_them_quack2(&q3, &q4);
//Wolf w1, w2;
//let_them_quack2(&w1, &w2); // ERROR: constraints not satisfied
}
выход:
Quack!Quack!Quack2!Quack2!
Как видите, вы сможете: omit writing a template parameter list
ItQuacks довольно явно так types are never used and that it's only the interface that matters
происходит. это I'd like to have sort of an interface annotation/check.
также имеет место, использование концепции также даст вам значимое сообщение об ошибке.
Нам нужно написать только одну версию функции:
#include <utility>
template<typename... Quackers>
void let_them_quack(Quackers&& ...quackers) {
using expand = int[];
void(expand { 0, (std::forward<Quackers>(quackers).quack(), 0)... });
}
struct Duck {
void quack() {}
};
int main()
{
Duck a, b, c;
let_them_quack(a, b, c, Duck());
}