C++03 では、Boost の Foreach は、この興味深い手法を使用して、式が左辺値か右辺値かを実行時に検出できます。(私はこの StackOverflow question: Rvalues in C++03を介してそれを見つけました)
これが実行時に動作するデモです
(これは、私の最近の別の質問について考えているときに発生した、より基本的な質問です。これに対する回答は、他の質問の回答に役立つ可能性があります。)
コンパイル時に C++03 で rvalue-ness をテストするという質問を詳しく説明したので、これまで試してきたことについて少しお話しします。
コンパイル時にこのチェックを実行できるようにしたい。C++11なら簡単だけど、C++03が気になる。
私は彼らのアイデアに基づいて構築しようとしていますが、さまざまなアプローチにもオープンです。彼らの手法の基本的な考え方は、このコードをマクロに入れることです。
true ? rvalue_probe() : EXPRESSION;
の左側が「true」である?
ため、EXPRESSION が評価されることはないと確信できます。しかし興味深いのは?:
、パラメーターが左辺値か右辺値かによって、演算子の動作が異なることです (詳細については、上記のリンクをクリックしてください)。rvalue_probe
特に、 EXPRESSION が左辺値であるかどうかに応じて、次の 2 つの方法のいずれかでオブジェクトを変換します。
struct rvalue_probe
{
template< class R > operator R () { throw "rvalue"; }
template< class L > operator L & () const { throw "lvalue"; }
template< class L > operator const L & () const { throw "const lvalue"; }
};
スローされたテキストをキャッチして、EXPRESSION が左辺値か右辺値かを分析するために使用できるため、これは実行時に機能します。しかし、コンパイル時にどの変換が使用されているかを特定する方法が必要です。
これは潜在的に有用です。
EXPRESSION は右辺値ですか?
私たちは尋ねることができます:
コンパイラがtrueをコンパイルしているとき rvalue_probe() : EXPRESSION、オーバーロードされた 2 つの演算子
operator X
またはoperator X&
のどちらが選択されていますか?
(通常、戻り値の型を変更してそれを取得するsizeof
ことで、どのメソッドが呼び出されたかを検出できます。しかし、これらの変換演算子では、特にそれらが?:
.
私は次のようなものを使用できるかもしれないと思った
is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type
EXPRESSION が左辺値の場合、operator&
が選択され、式全体が型になることを期待しました&
。しかし、うまくいかないようです。ref 型と非 ref 型を区別するのは非常に困難 (不可能?) です。特に、?:
どの変換が選択されたかを確認するために式の内部を掘り下げようとしている今はそうです。
ここに貼り付けたデモコードは次のとおりです。
#include <iostream>
using namespace std;
struct X {
X(){}
};
X x;
X & xr = x;
const X xc;
X foo() { return x; }
const X fooc() { return x; }
X & foor() { return x; }
const X & foorc() { return x; }
struct rvalue_probe
{
template< class R > operator R () { throw "rvalue"; }
// template< class R > operator R const () { throw "const rvalue"; } // doesn't work, don't know why
template< class L > operator L & () const { throw "lvalue"; }
template< class L > operator const L & () const { throw "const lvalue"; }
};
typedef int lvalue_flag[1];
typedef int rvalue_flag[2];
template <typename T> struct isref { static const int value = 0; typedef lvalue_flag type; };
template <typename T> struct isref<T&> { static const int value = 1; typedef rvalue_flag type; };
int main() {
try{ true ? rvalue_probe() : x; } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : xc; } catch (const char * result) { cout << result << endl; } // Y const lvalue
try{ true ? rvalue_probe() : xr; } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : foo(); } catch (const char * result) { cout << result << endl; } // Y rvalue
try{ true ? rvalue_probe() : fooc(); } catch (const char * result) { cout << result << endl; } // Y rvalue
try{ true ? rvalue_probe() : foor(); } catch (const char * result) { cout << result << endl; } // Y lvalue
try{ true ? rvalue_probe() : foorc(); } catch (const char * result) { cout << result << endl; } // Y const lvalue
}
(最後に別のコードがいくつかありましたが、それは単に混乱を招くだけです。失敗した回答の試行を実際に見たくはありません! 上記のコードは、実行時に lvalue-vsus-rvalue をテストする方法を示しています。)