std::as_const と sprout::as_cont の違い
std::as_const
と sprout::as_const
は、何れも引数を const
修飾された型に変換する関数テンプレートである。尚、 std::as_const
はC++17にむけて提案されている関数であるため、実装されている環境は少ない。
Constant View: A proposal for a std::as_const helper function template
sprout::as_const
はSprout全体において運用される、処理をコンパイル時に行うための記述が含まれるが、それについては本記事の主旨ではないため触れない*1。
std::as_const
と sprout::as_const
の最も顕著な違いは、 sprout::as_const
は実引数が rvalue
の時、戻り値の型が rvalue reference
になるのに対し、
std::as_const
は実引数が rvalue
であっても戻り値の型は lvalue reference
となる。
rvalue
対応版
#include <type_traits> #include <iostream> #include <utility> #include <boost/type_index.hpp> using namespace std; using boost::typeindex::type_id_with_cvr; template< typename Arg > std::conditional_t < std::is_lvalue_reference< Arg >::value, std::add_lvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > >, std::add_rvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > > > inline as_const( Arg&& arg )noexcept { return std::forward< Arg >( arg ); } template< typename Arg > void f( Arg&& arg ) { cout << "arg is " << type_id_with_cvr< decltype( arg ) >() << endl; } int main() { int a; f( a ); f( move( a ) ); f( as_const( a ) ); f( as_const( move( a ) ) ); }
出力
Start arg is int& arg is int&& arg is int const& arg is int const&& 0 Finish
当然ながら、 std::forward
や std::move
を適切に使用する必要がある。
lvalue
版
... template< typename Arg > std::add_lvalue_reference_t< std::add_const_t< std::remove_reference_t< Arg > > > inline as_const( Arg&& arg )noexcept { return std::forward< Arg >( arg ); } ...
出力
Start arg is int& arg is int&& arg is int const& arg is int const& 0 Finish
何れも簡易的な実装であり、提案された文書とは実装が異なる。
初期の提案では右辺値を渡された場合の変数の寿命延長について考慮し、 右辺値を受け取った場合はコピー演算を行うバージョンについても記述されていたが、 最新の文書では無くなっているため問題ないと判断されたのであろう。
右辺値をどう扱うかの違いによって生じる影響については機会があればまた論じたい。
私はSproutの右辺値も扱えるバージョンが好きだな。
*1:というか私が理解してない