Во-первых, «ref-qualifiers for * this» - это просто «маркетинговое заявление». Тип *this
никогда не меняется, смотрите в нижней части этого поста. Это намного проще понять с помощью этой формулировки.
Затем следующий код выбирает функцию для вызова на основе ref-квалификатора «параметра неявного объекта» функции † :
// 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
Все это сделано для того, чтобы вы могли использовать тот факт, что объект, для которого вызывается функция, является значением r (например, безымянный временный объект). Возьмем следующий код в качестве дополнительного примера:
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-квалификаторы ( &
и &&
).
Примечание: многие стандартные кавычки и объяснение разрешения перегрузки приведены здесь!
† Чтобы понять, как это работает, и почему ответ @Nicol Bolas хотя бы частично неверен, нам нужно немного покопаться в стандарте C ++ (часть, объясняющая, почему ответ @ Nicol неправильный, находится внизу, если вы интересует только это).
Какая функция будет вызываться, определяется процессом, называемым разрешением перегрузки . Этот процесс довольно сложный, поэтому мы коснемся только той части, которая важна для нас.
Во-первых, важно посмотреть, как работает разрешение перегрузки для функций-членов:
§13.3.1 [over.match.funcs]
p2 Набор функций-кандидатов может содержать как функции-члены, так и функции, не являющиеся членами, которые должны быть разрешены для одного и того же списка аргументов. Чтобы списки аргументов и параметров были сопоставимы в этом гетерогенном наборе, считается , что функция-член имеет дополнительный параметр, называемый параметром неявного объекта, который представляет объект, для которого была вызвана функция-член . [...]
p3 Точно так же, когда это уместно, контекст может создать список аргументов, который содержит подразумеваемый аргумент объекта для обозначения объекта, с которым нужно работать.
Почему нам даже нужно сравнивать функции-члены и не-члены? Перегрузка оператора, вот почему. Учти это:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
Вы наверняка хотели бы, чтобы следующее вызывало функцию free, не так ли?
char const* s = "free foo!\n";
foo f;
f << s;
Вот почему функции-члены и не-члены включены в так называемый набор перегрузки. Чтобы сделать решение менее сложным, существует жирная часть стандартной цитаты. Кроме того, это важный бит для нас (тот же пункт):
p4 Для нестатических функций-членов тип неявного параметра объекта:
«Lvalue ссылка на cv X
» для функций, объявленных без квалификатора ref или с &
квалификатором ref
«Rvalue ссылка на 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
квалифицированным, значение r может быть привязано к параметру, если во всех других отношениях аргумент может быть преобразован в тип параметра неявного объекта.
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
это всегда именующий:
§5.3.1 [expr.unary.op] p1
Унарный *
оператор выполняет косвенное обращение: выражение, к которому он применяется, должно быть указателем на тип объекта или указателем на тип функции, а результатом является lvalue, указывающее на объект или функцию, на которые указывает выражение.
§9.3.2 [class.this] p1
В теле нестатической (9.3) функции-члена ключевое слово this
является выражением prvalue, значением которого является адрес объекта, для которого вызывается функция. Тип this
функции-члена класса X
- X*
. [...]