недопустимая инициализация неконстантной ссылки из rvalue

Итак, у меня есть следующая функция:

void scan(std::istream& is, Handler& h);

Я хочу называть это по-разному, например:

scan(std::cin, Handler());
scan(std::ifstream("myfile"), myhandler);

Компилятор жалуется на то, что std::ifstream("myfile") и Handler() являются значениями r, которые передаются как неконстантные ссылки, поэтому жалоба законна, но что я могу сделать?

  1. Ни один из параметров функции не может быть const (istream модифицируется при чтении, а обработчик меняет свое состояние во время обратных вызовов).
  2. Если я изменю типы параметров на ссылки rvalue (&&), то я не смогу передать std::cin, а иногда мне действительно важно конечное состояние myhandler, поэтому я не могу применить к ним и std::move.
  3. В принципе, я мог бы сделать параметры универсальными ссылками с помощью шаблона или вывода типа auto&& и, таким образом, перегрузить эту функцию для всех возможных комбинаций ссылок lvalue и rvalue, но я не собираюсь перегружать эту функцию для других типов, кроме тех, которые я уже указал.

Есть ли другие варианты?

Как-то вся эта семантика ходов мешала в таком тривиальном примере.


person mariusm    schedule 16.02.2016    source источник
comment
std::ifstream("myfile") — это временное значение, почему бы просто не создать для него переменную?   -  person πάντα ῥεῖ    schedule 16.02.2016
comment
Я знаю, что это временно и, следовательно, rvalue, но могу ли я преобразовать его в lvalue ref?   -  person mariusm    schedule 16.02.2016
comment
Содержит ли scan ссылки/указатели на объекты потока после его возврата? Если нет, вы можете сделать std::ifstream("myfile") >> std::skipws, чтобы получить ссылку lvalue на временный файл.   -  person 0x499602D2    schedule 16.02.2016
comment
Это не имеет ничего общего с семантикой перемещения.   -  person Edward Strange    schedule 16.02.2016
comment
@ 0x499602D2 Я вижу, что вы там сделали, но оператор >> изменяет состояние потока, и тот же трюк не применим к Handler(). Для меня это проблема языка: внезапно я вынужден объявить некоторые временные переменные только для хранения ссылок...   -  person mariusm    schedule 16.02.2016
comment
@CrazyEddie, если я попытаюсь уйти с && (т.е. позволить ему использовать оба аргумента), тогда это так.   -  person mariusm    schedule 16.02.2016
comment
template<class T>T& lvalue_ref(T&& x){return x;} потом позже - scan(lvalue_ref(ifstream()), lvalue_ref(Handler()))   -  person 0x499602D2    schedule 16.02.2016
comment
@ πάνταῥεῖ Я должен добавить, что некоторые вызовы находятся внутри switch-cases, поэтому, если я объявляю новую переменную в этой области, компилятор жалуется, что она пересекает инициализацию.   -  person mariusm    schedule 16.02.2016
comment
@mariusm Это веская причина для использования указателя вместо ссылки. Также вы можете создавать переменные в блоке switch/case, помещая их в блок области действия ({}).   -  person πάντα ῥεῖ    schedule 16.02.2016
comment
@ 0x499602D2 ваш шаблон lvalue_ref кажется решением, которое я искал. Странно, что это не часть std:: :-)   -  person mariusm    schedule 16.02.2016
comment
Почему бы вам не ограничить шаблон функции?   -  person Piotr Skotnicki    schedule 16.02.2016
comment
@PiotrSkotnicki это много утомительного кода, я надеялся на более простое решение.   -  person mariusm    schedule 16.02.2016


Ответы (1)


Чтобы преобразовать rvalue в lvalue, вы можете использовать эту вспомогательную функцию lvalue:

template<class T>
T& lvalue_ref(T&& x) { return x; }

И тогда вызов становится:

scan(lvalue_ref(std::ifstream("myfile")), lvalue_ref(Handler()));

Это безопасно, так как временные (ifstream и Handler) не уничтожаются до конца полного выражения. Однако обратите внимание, что это ссылки lvalue на временные, и поэтому вы должны соблюдать осторожность при принятии решения об использовании этого метода. Я предполагаю, что scan() не содержит ссылок/указателей на аргументы после возврата.

Например, не используйте его так:

int& x = lvalue_ref(5);
std::cout << x; // temporary is destructed, therefore Undefined Behavior

Просто убедитесь, что время жизни возвращаемой ссылки соответствует времени жизни временной, и все будет в порядке.

person 0x499602D2    schedule 16.02.2016
comment
Очень приятно, а также объяснение, почему это вообще не поощряется. Спасибо! - person mariusm; 16.02.2016