Инициализация ссылки lvalue с rvalue

Я построил этот код с помощью gcc/clang и получил разные результаты:

#include <iostream>
#include <sstream>

int main() {
    std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
}
  • Почему gcc позволяет инициализировать ссылку lvalue с rvalue (std::stringstream(""))?
  • Почему clang пытается вызвать конструктор копирования?

gcc 4.9.1

Ошибка

clang 3.4

prog.cc:5:63: error: call to implicitly-deleted copy constructor of 'istream' (aka 'basic_istream<char>')
    std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                                                             ^~~~~~~~
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: copy constructor is implicitly deleted because 'basic_istream<char, std::__1::char_traits<char> >' has a user-declared move constructor
   basic_istream(basic_istream&& __rhs);
   ^
prog.cc:5:28: error: calling a protected constructor of class 'std::__1::basic_istream<char, std::__1::char_traits<char> >'
   std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                          ^
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: declared protected here
   basic_istream(basic_istream&& __rhs);
   ^
prog.cc:5:28: error: calling a protected constructor of class 'std::__1::basic_istream<char, std::__1::char_traits<char> >'
   std::istream& is = 1 ? std::move(std::stringstream("")) : std::cin;
                          ^
/usr/local/libcxx-3.4/include/c++/v1/istream:185:5: note: declared protected here
   basic_istream(basic_istream&& __rhs);
   ^

Ответ 1

Поведение GCC - ошибка, и исправлено на соединительной линии. Кланг верен. Это беспорядочный случай, потому что у вас есть смешанные категории значений для второго и третьего операндов условного оператора:

  • std::move(std::stringstream("")) - это значение xvalue * типа std::stringstream;
  • std::cin - это значение типа std::istream.

Соответствующая стандартная цитата (§5.16 [expr.cond]/p3-6) находится в этом ответе. Это достаточно долго, что я действительно не хочу его копировать. Я просто расскажу, как он применяется к этому коду:

  • Очевидно, что std::istream не может быть преобразован в соответствие std::stringstream любым способом, независимо от категории значений;
  • Значение x типа std::stringstream не может быть преобразовано в тип "ссылка lvalue на std::istream" с учетом ограничения, что ссылка должна привязываться непосредственно к lvalue - здесь нет значения для ссылки для привязки;
  • std::istream является базовым классом std::stringstream, поэтому на 3-ю пулю p3 значение x типа std::stringstream может и будет преобразовано в значение prvalue, временное типа std::istream, путем инициализации копирования, которое заменяет исходный операнд для дальнейшего анализа.
  • Теперь второй операнд является prvalue типа std::istream, третий операнд является lvalue типа std::istream, у них разные категории значений, поэтому p4 не применяется.
  • Следовательно, результатом является prvalue per p5. Поскольку они имеют один и тот же тип, разрешение перегрузки, указанное в p5, не выполняется, и вы переходите к p6.
  • Применимая пуля в p6

    Второй и третий операнды имеют один и тот же тип; результат этот тип. Если операнды имеют тип класса, результатом является значение prvalue временный тип результата, который инициализируется копированием из второй операнд или третий операнд в зависимости от значения первый операнд.

    поэтому он копирует-инициализирует результат (который является временным значением prvalue) из либо преобразованного первого операнда, либо второго операнда (std::cin).

Следовательно, ошибки:

  • Копирование-инициализация результата prvalue std::istream из lvalue (std::cin) будет использовать конструктор копирования, а потоки не могут быть скопированы.
  • Копировать-инициализацию временного значения prvalue std::istream для второго операнда из std::stringstream xvalue - это перемещение, но конструктор перемещения std::istream защищен.

* Для терминологии (lvalue, xvalue, prvalue и т.д.), см. Что такое rvalues, lvalues, xvalues, glvalues ​​и prvalues?суб >