Шаблоны C++ обычно уподобляются создателям наворотов, и идея с оболочкой имеет дело именно с этим:, делая шаблон просто тонкой оболочкой над обычной функцией. Это действительно отличный способ уменьшить вздутие живота.
Например, давайте воспользуемся простой прокладкой:
//
// Shim interface
//
struct Interface {
virtual void print(std::ostream& out) const = 0;
}; // struct Interface
std::ostream& operator<<(std::ostream& out, Interface const& i) {
i.print(out);
return out;
}
template
struct IT: public Interface {
IT(T const& t): _t(t) {}
virtual void print(std::ostream& out) const { out << _t; }
T const& _t;
};
template
IT shim(T const& t) { return IT(t); }
Теперь я могу использовать ее вот так:
void print_impl(Interface const& t);
template
void print(T const& t) { print_impl(shim(t)); }
И независимо от того, как реализован print_impl
, print
остается очень легким и должен быть встроен. Очень просто.
Однако C++11 вводит вариативные шаблоны. Типичное побуждение тогда состоит в том, чтобы повторно реализовать все небезопасные C-variadic шаблоны C++11 variadic, даже Википедия предлагает a printf
реализацию .
К сожалению, реализация Википедии не имеет дело с позиционными аргументами:, такими, которые позволяют указать там print 3-й параметр и т.д... Было бы просто, если бы у нас была функция с этим прототипом:
void printf_impl(char const* format, Interface const* array, size_t size);
или похожие.
Теперь, , как нам соединить исходный интерфейс:
template
void printf(char const* format, T const&... t);
с сигнатурой выше?
Одна из трудностей с прокладками заключается в том, что они полагаются на привязку к поведению const-ref для продления времени жизни временной оболочки, созданной ровно настолько, чтобы не выделять память динамически (, они бы не ] быть дешевыми, если они сделали).
Тем не менее, кажется сложным получить эту привязку + преобразование массива за один шаг. Тем более, что в языке не разрешены массивы ссылок (и указатели на ссылки).
У меня есть начало решения, для тех, кто заинтересован:
//
// printf (or it could be!)
//
void printf_impl(char const*, Interface const** array, size_t size) {
for (size_t i = 0; i != size; ++i) { std::cout << *(array[i]); }
std::cout << "\n";
}
template
void printf_bridge(char const* format, T const&... t) {
Interface const* array[sizeof...(t)] = { (&t)... };
printf_impl(format, array, sizeof...(t));
}
template
void printf(char const* format, T const&... t) {
printf_bridge(format, ((Interface const&)shim(t))...);
}
однако вы заметите введение дополнительного шага, который немного раздражает. Тем не менее, это работает .
Я был бы очень признателен, если бы кто-нибудь предложил лучшую реализацию.
@Potatoswatter предложил использовать списки инициализаторов, что помогает немного(нет диапазона-для этого).
void printf_impl(char const*, std::initializer_list array) {
for (Interface const* e: list) { std::cout << *e; }
std::cout << "\n";
}
template
void printf_bridge(char const* format, T const&... t) {
printf_impl(format, {(&t)...});
}
Но по-прежнему не решает проблему промежуточной функции.