На самом деле, это очень хороший вопрос. До сих пор я также использовал трюк с универсальным эталоном плюс молоток enable_if
. Здесь я представляю решение, которое не использует шаблоны и использует приведение lvalue в качестве альтернативы.
Далее следует реальный пример, когда ситуация возникает с использованием известного примера использования ofstream
на месте, что невозможно (или очень сложно) в C++98 (я использую ostringstream
в примере, чтобы сделать его более понятным).
Сначала вы увидите функцию по ссылке lvalue, как это часто бывает в C++98.
#include<iostream>
#include<sstream>
struct A{int impl_;};
std::ostringstream& operator<<(std::ostringstream& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // possibly much longer code.
return oss;
}
// naive C++11 rvalue overload without using templates
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
oss << "A(" << a.impl_ << ")"; // ok, but there is code repetition.
return oss;
}
int main() {
A a{2};
{// C++98 way
std::ostringstream oss;
oss << a;
std::cout << oss.str() << std::endl; // prints "A(2)", ok"
}
{// possible with C++11, because of the rvalue overload
std::cout << (std::ostringstream() << a).str() << std::endl; //prints "A(2)", ok
}
}
Как видите, в C++11 мы можем добиться того, чего не можем в C++98. То есть использовать ostringstream
(или ofstream
) на месте. Теперь возникает вопрос OP, две перегрузки выглядят очень похожими, можно ли их объединить в одну?
Один из вариантов — использовать универсальную ссылку (Ostream&&
) и, при необходимости, enable_if
для ограничения типа. Не очень элегантно.
Что я обнаружил, используя этот пример «реального мира», так это то, что если вы хотите использовать один и тот же код для ссылки lvalue и ссылки rvalue, потому что, вероятно, вы можете преобразовать одно в другое!
std::ostringstream& operator<<(std::ostringstream&& oss, A const& a){
return operator<<(oss, a);
}
Это выглядит как бесконечно рекурсивная функция, но это не так, потому что oss
является ссылкой lvalue (да, это ссылка lvalue, потому что у нее есть имя). Таким образом, он вызовет другую перегрузку.
Вам по-прежнему нужно написать две функции, но у одной есть код, который вам не нужно поддерживать.
Таким образом, если «имеет смысл»© применять функцию как к (неконстантной) ссылке lvalue, так и к rvalue, это также означает, что вы можете преобразовать rvalue в lvalue и, следовательно, перенаправить к одной функции. Обратите внимание, что «это имеет смысл» зависит от контекста и значения предполагаемого кода, и это то, что мы должны «сказать» компилятору, явным образом вызвав перегрузку lvalue.
Я не говорю, что это лучше, чем использование универсальной ссылки, я говорю, что это альтернатива, и, возможно, намерение более ясно.
Редактируемый код здесь: http://ideone.com/XSxsvY. (обратная связь приветствуется)
person
alfC
schedule
04.02.2014
int
, по rvalue. В более общем случае, я считаю, что C++11 имеет для этого особый случай, когда параметр rvalue будет прочитан как параметр rvalue или lvalue. Я не могу вспомнить точную ситуацию, когда это применимо. - person Dave   schedule 04.05.2013x
было либо ссылкой на rvalue (если передается ссылка на rvalue), либо ссылкой на lvalue наconst
? Правильно ли я понимаю, чтоX&&
неприемлемо для вас, потому что это будет ссылка lvalue на не-const
при передаче lvalue? - person Andy Prowl   schedule 04.05.2013template<typename X> void operator () (X&& x)
, вам будет разрешено передавать любые параметры (lvalues или rvalues). Учитывая аргумент типаU
, вывод типа сделаетx
U&& x
в случае, если аргумент является rvalue, иU& x
в случае, если аргумент является lvalue - person Andy Prowl   schedule 04.05.2013const
. - person Andy Prowl   schedule 04.05.2013