altebute.hatenablog.com

犬も歩けば規格にあたる

std::as_const と sprout::as_cont の違い

std::as_constsprout::as_const は、何れも引数を const 修飾された型に変換する関数テンプレートである。尚、 std::as_constC++17にむけて提案されている関数であるため、実装されている環境は少ない。

Constant View: A proposal for a std::as_const helper function template

sprout::as_const はSprout全体において運用される、処理をコンパイル時に行うための記述が含まれるが、それについては本記事の主旨ではないため触れない*1

std::as_constsprout::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::forwardstd::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:というか私が理解してない