В C++0x планируется добавление интересной штуки под названием rvalue references, которая должна убрать изрядную часть накладных расходов в C++ программах. Но пока C++0x не принят, приходится жить без rvalue references и изобретать разные вспомогательные велосипедики.
Первый, под названием temporary_object_ref, я начал использовать года полтора назад. Штука тривиальная и, временами, полезная. Но не всегда. Представим себе, что у нас есть объект, конструктор которого получает три сложных аргумента:
class demo_t { private : std::string m_destination; std::string m_source; std::list< std::string > m_hints; public : demo_t( const std::string & destination, const std::string & source, const std::list< std::string > & hints ) : m_destination( destination ) , m_source( source ) , m_hints( hints ) {} ... }; |
Если мы используем только этот конструктор, то мы всегда вынуждаем объект demo_t копировать значения аргументов. Даже в случаях, когда аргументами являются временные объекты:
std::string destination = domain + "/" + subject + "/" + timestamp; std::list< std::string > hints; hints.push_back( "lifetime: default" ); hints.push_back( "priority: default" ); demo_t demo( destination, source, hints ); |
В этом случае временные объекты destination и hints после объявления demo не нужны. И было бы хорошо, если бы при инициализации demo их значения перешли бы объекту demo (та самая move semantic из C++0x).
Этого можно было бы достичь с помощью temporary_object_ref, если добавить в demo_t еще один конструктор:
class demo_t { ... public : ... // Остальные конструкторы. demo_t( temporary_object_ref_t< std::string > destination, const std::string & source, temporary_object_ref_t< std::list< std::string > > hints ) : m_source( source ) { destination.writeable().swap( m_destination ); hints.writeable().swap( m_hints ); } ... }; ... demo_t demo( make_temporary_object_ref( destination ), source, make_temporary_object_ref( hints ) ); |
Но здесь уже видна проблема – нам придется писать столько вариантов конструктора demo_t, сколько возможных сочетаний временных и константных аргументов встретится в программе. Понятно, что это тупиковый путь.
На днях меня посетила идея, как можно обойтись всего одним конструктором demo_t, но при этом иметь возможность забирать значения из временных аргументов:
class demo_t { ... public : demo_t( const_or_swappable_t< std::string > destination, const_or_swappable_t< std::string > source, const_or_swappable_t< std::list< std::string > > hints ) { destination.apply_value( m_destination ); source.apply_value( m_source ); hints.apply_value( m_hints ); } ... }; ... demo_t demo( swappable_object( destination ), const_object( source ), swappable_object( hints ) ); |
Весь фокус в простом шаблоне const_or_swappable_t (код которого будет приведен ниже). Если объекту const_or_swappable_t передается константный аргумент, то в своем методе apply_value он выполняет его копирование. Если же аргументом является временный объект – то в apply_value вызывается swap. Тип аргумента (константный или временный) указывает программист посредством вспомогательных функций const_object и swappable_object.
Disclaimer. Пока это решение на практике я еще ни разу не применил. Самым явным его недостатком является то, что при его использовании сначала вызываются конструкторы по умолчанию для атрибутов класса, а только затем отрабатывают apply_value. Это так же лишние накладные расходы. А так же нельзя инициализировать подобным образом атрибуты-константы.
А вот код const_or_swappable_t с необходимыми вспомогательными функциями:
struct constant_object_tag {}; struct swappable_object_tag {}; template< class T > class const_or_swappable_t { union { const T * const_ptr; T * swappable_ptr; } m_ptr; bool m_is_const; public : const_or_swappable_t( constant_object_tag, const T & o ) : m_is_const( true ) { m_ptr.const_ptr = &o; } const_or_swappable_t( swappable_object_tag, T & o ) : m_is_const( false ) { m_ptr.swappable_ptr = &o; } void apply_value( T & to ) const { if( m_is_const ) to = *(m_ptr.const_ptr); else m_ptr.swappable_ptr->swap(to); } }; template< class T > const_or_swappable_t< T > const_object( const T & o ) { return const_or_swappable_t< T >( constant_object_tag(), o ); } template< class T > const_or_swappable_t< T > swappable_object( T & o ) { return const_or_swappable_t< T >( swappable_object_tag(), o ); } |
Комментариев нет:
Отправить комментарий