Во-первых, «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
Все сделано для того, чтобы вы могли использовать тот факт, что объект, для которого вызывается функция, является значением 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-qualifier или с &
ref-qualifier
«rvalue ссылка на cv X
» для функций, объявленных с квалификатором &&
где 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-qualifier (и, следовательно, не перегружали функцию), то f1
будет соответствовать значению r (по-прежнему §13.3.1
) ):
p5 [...] Для нестатических функций-членов, объявленных без ref-qualifier , применяется дополнительное правило:
- , даже если неявный параметр объекта не является
const
-квалифицированным, значение r может быть привязано к параметру при условии, что во всех других отношениях аргумент может быть преобразован в тип параметра неявного объекта.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Теперь о том, почему @ Николь ответ по крайней мере частично неверен. Он говорит:
Обратите внимание, что это объявление меняет тип *this
.
Это неправильно, *this
- это всегда lvalue:
§5.3.1 [expr.unary.op] p1
Унарный оператор *
выполняет косвенное обращение : выражение, к которому он применяется, должно быть указателем на тип объекта или указателем на тип функции , а результатом является lvalue ссылается на объект или функцию, на которые указывает выражение.
§9.3.2 [class.this] p1
В теле нестатической (9.3) функции-члена ключевое слово this
является выражением prvalue, значением которого является адрес объекта, для которого вызывается функция. Тип this
в функции-члене класса X
равен X*
. [...]