Дано:
#include <iostream>
#include <functional>
template<class T> // Just for overloading purposes
struct behaviour1 : std::reference_wrapper<T const>
{
using base_t = std::reference_wrapper<T const>;
using base_t::base_t;
// This wrapper will never outlive the temporary
// if used correctly
behaviour1(T&& t) : base_t(t) {}
};
template<class T>
behaviour1(T&&) -> behaviour1<std::decay_t<T> >;
struct os_wrapper : std::reference_wrapper<std::ostream>
{
using std::reference_wrapper<std::ostream>::reference_wrapper;
template<class T>
os_wrapper& operator<<(behaviour1<T> const& v)
{
*this << v.get(); // #1
return *this;
}
template<class U>
os_wrapper& operator<<(U&& t)
{ this->get() << t; return *this; }
};
int main() { os_wrapper(std::cout) << behaviour1(3); }
В строке # 1 фактически выполняется косвенное перенаправление, учитывая тот конкретный вариант использования (оболочка, которая сразу же разворачивается и не копируется, не привязана к локальным ссылкам, кроме параметров функции), или компилятор просто обнаружит, что объект сразу же развернут и просто использовать оригинальный объект вместо этого? В противном случае, что будет лучшим дизайном (например, будет ли более эффективной частичная специализация для скалярных типов, которые просто сохраняют копию объекта)?
Я заинтересован конкретно в gcc
(версия 7, -O3 -std=c++17
) хотя думаю оба clang
а также gcc
выполняет аналогичные методы оптимизации.
Ожидаемая стоимость использования методов std::reference_wrapper
в оптимизированной сборке 0, потому что все std::reference_wrapper
код может быть встроен. Сборка вывода постановки os_wrapper(std::cout) << behaviour1(3);
является:
movl $3, %esi
movl std::cout, %edi
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
Встраивание — это то, что позволяет использовать абстракции без накладных расходов, которые любит упоминать Страуструп.
Других решений пока нет …