Ответ 1
Во-первых, "ref-qualifiers for * this" является просто "маркетинговым выражением". Тип *this
никогда не изменяется, см. Нижнюю часть этого сообщения. Легче понять это с помощью этой формулировки.
Далее, следующий код выбирает функцию, которая должна быть вызвана на основе ref-qualifier "неявного параметра объекта" функции †:
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
Вывод:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
Все это сделано для того, чтобы вы могли воспользоваться тем фактом, когда объект, вызываемый функцией, является rvalue (например, неназванным временным). В качестве дополнительного примера возьмем следующий код:
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
Это может быть немного надуманным, но вы должны получить эту идею.
Обратите внимание, что вы можете комбинировать cv-квалификаторы (const
и volatile
) и ref-qualifiers (&
и &&
).
Примечание. Многие стандартные цитаты и описание разрешения перегрузки после этого!
† Чтобы понять, как это работает, и почему ответ @Nicol Bolas, по крайней мере, частично ошибочен, нам нужно немного выкопать в стандарте С++ (часть, объясняющая, почему ответ @Nicol неверен, внизу, если вас это интересует только).
Какую функцию вызывается, определяется процессом, называемым разрешением перегрузки. Этот процесс довольно сложный, поэтому мы будем касаться только того, что важно для нас.
Во-первых, важно посмотреть, как работает разрешение перегрузки для функций-членов:
§13.3.1 [over.match.funcs]
p2 Набор функций-кандидатов может содержать как функции-члены, так и не-члены, которые должны быть разрешены в отношении одного и того же списка аргументов. Таким образом, список аргументов и параметров сопоставим в этом гетерогенном наборе, считается, что функция-член имеет дополнительный параметр, называемый неявным параметром объекта, который представляет объект, для которого функция-член была вызвана. [...]
p3 Аналогично, в случае необходимости контекст может построить список аргументов, который содержит подразумеваемый аргумент объекта для обозначения объекта, который будет использоваться.
Почему нам даже нужно сравнивать функции-члены и не-члены? Поэтому перегрузка оператора. Рассмотрим это:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
Вы, конечно, хотите, чтобы следующее вызывало свободную функцию, не так ли?
char const* s = "free foo!\n";
foo f;
f << s;
Вот почему функции-члены и не-члены включены в так называемый набор перегрузки. Чтобы сделать разрешение менее сложным, существует полужирная часть стандартной цитаты. Кроме того, это важный бит для нас (то же предложение):
p4 Для нестатических функций-членов тип параметра неявного объекта
"lvalue reference to cv
X
" для функций, объявленных без ref-qualifier или с&
ref-qualifier"rvalue reference to cv
X
" для функций, объявленных с&&
ref-qualifierгде
X
- класс, членом которого является член, а cv - cv-квалификация в объявлении функции-члена. [...]p5 Во время разрешения перегрузки [...] [t] он неявный параметр объекта [...] сохраняет свою идентичность, поскольку преобразования в соответствующем аргументе должны подчиняться этим дополнительным правилам:
не может быть введен временный объект для хранения аргумента для параметра неявного объекта; и
никакие пользовательские преобразования не могут применяться для достижения соответствия типа с ним
[...]
(Последний бит означает, что вы не можете обмануть разрешение перегрузки на основе неявных преобразований объекта, на который вызывается функция-член (или оператор).)
Возьмем первый пример в верхней части этого сообщения. После вышеупомянутого преобразования набор перегрузки выглядит примерно так:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
Затем список аргументов, содержащий подразумеваемый аргумент объекта, сопоставляется с списком параметров каждой функции, содержащейся в наборе перегрузки. В нашем случае список аргументов будет содержать только этот аргумент объекта. Посмотрим, как это выглядит:
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
Если после проверки всех перегрузок в наборе остается только один, разрешение перегрузки сработало и вызывается функция, связанная с этой преобразованной перегрузкой. То же самое касается второго вызова "f":
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
Обратите внимание, однако, что если бы мы не предоставили какой-либо ref-квалификатор (и как таковой не перегружали функцию), то f1
будет соответствовать rvalue (еще §13.3.1
):
p5 [...] Для нестатических функций-членов, объявленных без ref-квалификатора, применяется дополнительное правило:
- даже если неявный параметр объекта не
const
-qualified, rvalue может быть привязан к параметру, если во всех других отношениях аргумент может быть преобразован в тип неявного параметра объекта.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Теперь о том, почему ответ @Nicol по крайней мере частично ошибочен. Он говорит:
Обратите внимание, что это объявление изменяет тип
*this
.
Это неверно, *this
всегда имеет значение lvalue:
§5.3.1 [expr.unary.op] p1
Унарный оператор
*
выполняет косвенное обращение: выражение, к которому оно применяется, должно быть указателем на тип объекта или указателем на тип функции , а результатом является значение lvalue, ссылающееся на объект или функция, на которые указывает выражение.
§9.3.2 [class.this] p1
В теле нестатической (9.3) функции-члена ключевое слово
this
является выражением prvalue, значением которого является адрес объекта, для которого вызывается функция. Типthis
в функции-члене классаX
равенX*
. [...]