шаблоны — Статическая печать утки в переполнении стека

C ++ имеет некоторую типизацию утилит для типов, заданных параметрами шаблона. Мы понятия не имеем, какого типа DUCK1 а также DUCK2 будет, но пока они могут quack(), он скомпилирует и запустит:

template <class DUCK1, class DUCK2>
void let_them_quack(DUCK1* donald, DUCK2* daisy){
donald->quack();
daisy->quack();
}

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

  1. Я бы хотел пропустить написание списка параметров шаблона, который бы повторялся и был практически бессмысленным (представьте себе, что произойдет, если будет 7 уток …)
  2. Я хотел бы сделать несколько более ясным, что типы никогда не используются и что важен только интерфейс.
  3. Я хотел бы иметь вид интерфейса аннотации / проверки. Сделайте как-то ясно, какой интерфейс ожидается за типом. (Это, однако, немного в отличие от печати утки.)

Предлагает ли C ++ какие-либо функции для реализации одной или нескольких из трех идей?

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

7

Решение

По вопросам 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 ++; Вы можете найти один в увеличение.

10

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

Я хотел бы пропустить написание списка параметров шаблона, который повторяется
и в основном бессмысленно (только представьте, что будет, если будет 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)...);
}

Live Demo

5

# 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)...);
}
1

Вы сможете сделать его более привлекательным с концепцией (еще не в стандарте — но очень близко):

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 listItQuacks довольно явно так types are never used and that it's only the interface that matters происходит. это I'd like to have sort of an interface annotation/check. также имеет место, использование концепции также даст вам значимое сообщение об ошибке.

1

Нам нужно написать только одну версию функции:

#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());
}
0
По вопросам рекламы [email protected]