MSVC++ 2013 позволяет присваивать временные объекты, эффективно обрабатывая их как lvalues

Я сталкивался с этой "особенностью" в MSVC++, и теперь я не уверен, является ли это ошибкой или мое понимание lvalues ​​/rvalues ​​в C++ просто неверно.

Для иллюстрации я добавил немного заглушенного кода, но в основном проблема в том, что MSVC++ 2013 (как базовый, так и компилятор CTP NOV 2013) позволяет присваивать временные объекты, которые должны быть действительно значениями и, следовательно, запрещать любые попытки присваивания во время компиляции.,

#include <iostream>

struct account {
    int value;

    explicit account(int v) : value{ v } { 
        std::cout << "account ctor: " << value << std::endl; 
    }
    account(const account & acc) : value{ acc.value } { 
        std::cout << "account copy ctor" << std::endl; 
    }
    account(account && acc) : value{ acc.value } { 
        std::cout << "account move ctor" << std::endl; 
    }
    account & operator=(const account & acc) {
        value = acc.value;
        std::cout << "account copy assign" << std::endl;
        return *this;
    }
    account & operator=(account && acc) {
        value = acc.value;
        std::cout << "account move assign" << std::endl;
        return *this;
    }   
};

int get_int() { return 42; }

account get_account() {
    return account(123);
}

int main() {
    //get_int() = 5;    // this predictably fails to compile
                        // with '=' : left operand must be l-value

    // everything below succeeds
    get_account() = account(42);    // console trace for this 
                                    // account ctor: 42
                                    // account ctor: 123
                                    // account move assign

    account(123) = account(42);     // console trace same as above

    account acc(0);                 // account ctor: 0
    account(42) = acc;              // account ctor: 42
                                    // account copy assign

    get_account() = acc;            // console trace same as above
}

конечно get_account() = acc; или же account(42) = acc; не предписанное поведение C++ Standard?! И то и другое get_account() & account(42) должно привести к значениям, которые по определению не позволяют присваивания.

Кстати, перегрузка функций-членов на основе квалификаторов lvalue / rvalue

...
void memberFn() const &;
void memberFn() &&;
...

который поддерживается в NOV 2013 CTP не работает должным образом или вообще. Я предполагаю, что это результат неспособности распознать значения, так что this всегда lvalue.

PS К сожалению, у меня нет возможности проверить это с другими компиляторами.

1 ответ

Решение

Насколько я понимаю, это совершенно верно C++11.

Только встроенное присваивание prvalues ​​запрещено.

Из [5, expr]:

Примечание. Операторы могут быть перегружены, то есть заданы значения при применении к выражениям типа класса (раздел 9) или типа перечисления (7.2). Использование перегруженных операторов преобразуется в вызовы функций, как описано в 13.5. Перегруженные операторы подчиняются правилам синтаксиса, указанным в разделе 5, но требования типа операнда, категории значения и порядка вычисления заменяются правилами для вызова функции.

Итак, требования по

get_account() = account(42);

такие же, как и при любом другом вызове функции-члена

get_account().foo_bar(account(42));

что имеет смысл, так как это просто более хороший синтаксис для

get_account().operator=(account(42));

Раздел 3.10, посвященный Lvalues ​​и rvalues, ясно показывает это [basic.lval]:

Например, встроенные операторы присваивания ожидают, что левый операнд является lvalue, а правый операнд является prvalue, и в результате получается lvalue. Определяемые пользователем операторы являются функциями, а категории значений, которые они ожидают и получают, определяются их параметрами и типами возвращаемых данных.

Другие вопросы по тегам