C++03. Тест на rvalue-vs-lvalue во время компиляции, а не только во время выполнения

В C++03 Foreach от Boost, используя эту интересную технику, может определить во время выполнения, является ли выражение l-значением или r-значением. (Я нашел это через этот вопрос на StackOverflow: Rvalues in C++03 )

Вот демонстрация того, как это работает во время выполнения

(Это более фундаментальный вопрос, который возник, пока я размышлял над другим моим недавним вопросом. Ответ на него может помочь нам ответить на тот другой вопрос.)

Теперь, когда я сформулировал вопрос о проверке rvalue-ness в C++03 во время компиляции, я расскажу немного о том, что я пробовал до сих пор.

Я хочу иметь возможность делать эту проверку на этапе компиляции. Это легко сделать в C++11, но мне интересно, как это сделать в C++03.

Я пытаюсь развить их идею, но буду открыт и для других подходов. Основная идея их техники заключается в том, чтобы поместить этот код в макрос:

true ? rvalue_probe() : EXPRESSION;

Это 'true' слева от ?, и поэтому мы можем быть уверены, что EXPRESSION никогда не будет оценен. Но самое интересное, что оператор ?: ведет себя по-разному в зависимости от того, являются ли его параметры l-значениями или r-значениями (для подробностей щелкните по ссылке выше). В частности, он преобразует наш объект rvalue_probe одним из двух способов, в зависимости от того, является ли EXPRESSION l-значением или нет:

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 l-значением или r-значением. Но мне нужен какой-то способ определить, во время компиляции, какое преобразование используется.

Теперь, это потенциально полезно, потому что это означает, что вместо вопроса

Является ли EXPRESSION r-значением?

мы можем спросить:

Когда компилятор компилирует true ? rvalue_probe() : EXPRESSION, какой из двух перегруженных операторов, operator X или operator X&, выбран?

(Обычно можно было бы определить, какой метод был вызван, изменив типы возврата и получив sizeof. Но мы не можем сделать этого с этими операторами преобразования, особенно когда они похоронены внутри ?:. )

Я думал, что смогу использовать что-то вроде

is_reference< typeof (true ? rvalue_probe() : EXPRESSION) > :: type

Если ВЫРАЖЕНИЕ является l-значением, то выбирается оператор&, и я надеялся, что тогда все выражение будет типа &. Но, похоже, это не работает. Типы ref и non-ref довольно трудно (невозможно?) отличить, особенно теперь, когда я пытаюсь копаться внутри выражения ?:, чтобы увидеть, какое преобразование было выбрано.

Вот демонстрационный код, вставленный сюда:

#include 
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  struct isref     { static const int value = 0; typedef lvalue_flag type; };
template  struct isref { 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-versus-rvalue во время выполнения.)

22
задан Community 23 May 2017 в 10:28
поделиться