moveを阻害しないconstexprの文脈でのconstメンバ関数と非constメンバ関数の呼び分け
以下の記事と全く同じ事にハマった。
小ネタ - constexpr の文脈でconstメンバ関数と非constメンバ関数を呼び分ける
http://boleros.hateblo.jp/entry/20120331/1333187905
その際に、追加でハマった事を捕捉的に書いておく。
引数がrvalue referenceであった際に、そのままconst lvalue referenceにキャストしたgetメンバ関数の結果を返すと、アクセス先の値が実際にはconst修飾されていなくてもconst lvalue referenceが返る。その値をそのままmoveするとconst rvalue referenceが返るので、結果としてrvalue referenceを取るmoveコンストラクタは呼ばれず、多くの場合(const rvalue referenceを取る奇特なコンストラクタが無ければ)コピーコンストラクタが呼ばれることになり、効率の良いmoveを阻害する。const_castで元の型のlvalue referenceに戻してからmoveしてやると良さそう。
[追記]
こちらも参照
とくにあぶなくないRiSKのブログ - constexprとrvalue参照問題 http://sscrisk.hatenablog.com/entry/2014/05/31/constexpr%E3%81%A8rvalue%E5%8F%82%E7%85%A7%E5%95%8F%E9%A1%8C
以下サンプル
http://melpon.org/wandbox/permlink/zuEvu7XQEpQhdR0K
#include <type_traits> #include <iostream> template<typename T> struct X { T t; T& get() { return t; } constexpr T const& get() const { return t; } }; template<typename T> struct Y { Y(const T&) { std::cout << "copy" << std::endl; } Y(T&&) { std::cout << "move" << std::endl; } }; template<typename T> constexpr auto get(X<T>&& t) -> decltype(std::move(const_cast<X<T> const&>(t).get())) { return std::move(const_cast<X<T> const&>(t).get()); } template<typename T> constexpr auto improved_get(X<T>&& t) -> T&& { return std::move(const_cast<T&>(const_cast<X<T> const&>(t).get())); } int main() { static_assert(std::is_same< decltype(get( X<int>{1} )), const int&& >::value, ""); Y<int> y1((get( X<int>{1} ))); // moveを阻害する static_assert(std::is_same< decltype(improved_get( X<int>{1} )), int&& >::value, ""); Y<int> y2(improved_get( X<int>{1} )); // moveを阻害しない }