По-перше, "кваліфікаційні кваліфікації для цього" - це просто "маркетингова заява". Тип *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
Все це робиться для того, щоб ви могли скористатися тим фактом, коли об'єктом, за яким викликається функція, є ревальвінг (наприклад, неназваний тимчасовий). Візьміть наступний код як додатковий приклад:
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-kvalifiers ( &
і &&
).
Примітка: Багато стандартних цитат та пояснення роздільної здатності після перевантаження тут!
† Щоб зрозуміти, як це працює, і чому відповідь @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
Ви, звичайно, хочете, щоб зателефонували до безкоштовної функції, чи не так?
char const* s = "free foo!\n";
foo f;
f << s;
Ось чому функції членів і не членів включаються в так званий набір перевантажень. Щоб зробити резолюцію менш складною, існує смілива частина стандартної цитати. Крім того, це важливий біт для нас (той самий пункт):
p4 Для нестатичних функцій члена тип імпліцитного параметра об'єкта
«Саме посилання на сорти X
» для функцій , оголошена без рефов-класифікатором , або з &
рефом-класифікатором
"Rvalue посилання на cv X
" для функцій, оголошених за допомогою ref- kvalifier&&
де 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
Однак тут слід зазначити , що, якщо б ми не представили жодних реф-специфікатор (і як такої не перевантажена функцію), яка f1
буде збігатися з RValue ( до сих пір §13.3.1
):
p5 [...] Для нестатичних функцій-членів, оголошених без перекваліфікатора , застосовується додаткове правило:
- навіть якщо параметр неявного об'єкта не є
const
кваліфікованим, 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
це завжди значення:
§5.3.1 [expr.unary.op] p1
Одинарний *
оператор виконує непряме : вираз, до якого він застосовується, має бути вказівником на тип об'єкта або вказівником на тип функції, а результат - значенням, що посилається на об'єкт або функцію, на яку вказує вираз.
§9.3.2 [class.this] p1
У тілі нестатичної (9.3) функції члена ключове слово this
є першим виразом, значенням якого є адреса об'єкта, для якого викликається функція. Тип функції this
у члені класу X
є X*
. [...]