Более лаконичный способ использования прокладок в вариативных шаблонах?

Шаблоны 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)...});
}

Но по-прежнему не решает проблему промежуточной функции.

7
задан Matthieu M. 18 May 2016 в 14:33
поделиться