Я думаю о проблеме, которая имеет некоторое сходство с идеальной пересылкой, но где аргумент функции не передается вызываемой функции, а возвращается.Вот почему я называю это «идеальным проходом».
Проблема заключается в следующем:
Скажем, у нас есть функция, которая принимает объект по ссылке (и, возможно, некоторые дополнительные аргументы),изменяет этот объект и возвращает измененный объект.Наиболее известным примером таких функций, вероятно, являются operator<<
и operator>>
для iostreams.
Давайте использовать iostreams в качестве примера, потому что это позволяет красиво показать, что я ищу.Например, одна вещь, которую иногда хочется сделать:
std::string s = (std::ostringstream() << foo << bar << baz).str();
Конечно, это не работает по двум причинам:
std::ostringstream()
является r-значением, но operator<<
принимает l-значение в качестве первого аргумента.
operator<<
возвращает ostream&
(ну, по крайней мере, для стандартных на самом деле basic_ostream<CharT, Traits>&
, где CharT
и Traits
выводятся из первого аргумента).
Итак, давайте предположим, что мы хотим спроектировать оператор вставки так, чтобы вышеприведенный код работал (вы, очевидно, не можете сделать это длясуществующие операторы, но вы можете сделать это для ваших собственных классов).Очевидно, что решение должно иметь следующие характеристики:
Первый аргумент может принимать либо lvalue, либо rvalue.
Тип возвращаемого значения должен бытьтот же тип, что и переданный. Но, конечно, он все равно должен принимать только ostreams (то есть классы, полученные из экземпляров basic_ostream
).
Хотя в этом конкретном случае использования это не такПри необходимости я хочу добавить третье требование:
- Если первый аргумент является rvalue, то возвращается и значение, в противном случае возвращается lvalue.
Thisдополнительное правило заключается в том, что вы можете перемещать-конструировать из значения r, переданного через функцию (я не знаю, являются ли потоки C ++ 11 конструктивно-перемещаемыми, но предполагается, что это более общая схема, причем поток так же удобенпример).
Очевидно, что в C ++ 03 все эти требования не могут быть выполнены.Однако в C ++ 11 у нас есть rvalue-ссылки, которые должны сделать это возможным.
Вот моя попытка:
#include <iostream>
#include <sstream>
#include <string>
template<typename Ostream> struct is_ostream
{
typedef typename std::remove_reference<Ostream>::type candidate;
typedef typename candidate::char_type char_type;
typedef typename candidate::traits_type traits_type;
typedef std::basic_ostream<char_type, traits_type> basic_ostream;
static const bool value = std::is_base_of<basic_ostream, candidate>::value;
};
class SomeType {};
template<typename Ostream>
typename std::enable_if<is_ostream<Ostream>::value, Ostream&&>::type
operator<<(Ostream&& is, SomeType const& x)
{
is << "SomeType";
return std::forward<Ostream>(is);
}
int main()
{
SomeType t;
std::string s = (std::ostringstream() << t).str();
std::cout << s << std::endl;
}
Он действительно компилируется с gcc (используя опцию -std=c++0x
), а при запуске выдает SomeType
как положено.Тем не менее, это довольно сложный механизм, чтобы добраться туда.
Поэтому мои вопросы:
Является ли оператор вывода, как я написал, действительно работает, как ожидалось (и выполняет всетребования, которые я дал)?Или это может привести к неудаче неожиданным образом или иметь другие неожиданные последствия?Особенно: правильно ли я здесь использую std::forward
?
Есть ли более простой способ удовлетворить требования?
Предполагая, что это действительно такправильный и самый простой способ сделать это: считаете ли вы хорошей идеей это сделать, или посоветуете против этого (как специально для потокового вывода, как в моем примере, так и в качестве более общей схемы для передачи объектов через функции)?