Страницы

Поиск по вопросам

среда, 17 октября 2018 г.

std::move вне пространства имен std

Почему этот код работает, если даже если мы не используем пространство имен std?
#include #include #include
int main() { std::string s = "test"; std::cout << move(s); // <= move без std::, никакого using namespace std нет }
Однако move(1) не скомпилируется, тк нет функции move, а std::move(1) - да.
Проверено в g++6.3 -std=c++14, clang++5.0 и 6.0 -stdc++1z


Ответ

Это так называемый Argument-Dependent Lookup (ADL). Если кратко, то к функциям-кандидатам при поиске подходящей функции также добавляются функции из пространств имен фактических аргументов.
Рассмотрим простой пример (в конце имеется полный код):
namespace First { struct Type {};
void foo(Type) { std::cout << "First foo" << std::endl; }
void bar(First::Type, First::Type) { std::cout << "First::bar" << std::endl; }
}
namespace Second { struct Type {};
void foo(Type) { std::cout << "Second foo" << std::endl; }
void bar(First::Type, Second::Type) { std::cout << "Second::bar" << std::endl; }
}
int main() { First::Type f; Second::Type s;
//Аргумент из пространства имен First, //поэтому поиск будем производить и в пространстве имен First foo(f);
//Аргумент из пространства имен Second, //поэтому поиск будем производить и в пространстве имен Second foo(s);
//Один аргумент из пространства имен First, другой - из Second. //Поэтому поиск будет производиться и в First и в Second. //Но для вызова подходит только функция Second::bar bar(f, s); }
Рассмотрим более практический случай, когда ADL помогает делу. Возьмем простой шаблон функции, которая во время работы где-то в своих недрах обменивает значения двух своих аргументов:
template void zoo(T & one, T & two) { std::swap(one, two); }
zoo использует std::swap для обмена значений.
Представим, что два объекта типа First::Type можно обменять местами эффективнее, чем это делает std::swap. Нам необходимо, чтобы для типа First::Type эта функция использовала более эффективный обмен. Для начала необходимо добавить функцию swap в пространство имен First
namespace First { //... void swap(Type &, Type &) { std::cout << "First::swap" << std::endl; } }
Теперь необходимо как-то заставить zoo вызывать Second::swap вместо std::swap. В этом нам поможет ADL, мы просто убираем пространства имен при использовании swap
template void zoo(T & one, T & two) { swap(one, two); }
Теперь swap будет искаться в текущем пространстве имен и в пространстве имен своих аргументов. Но здесь возникает новая проблема. Что делать с типами, для которых нет swap? А для них мы будем использовать std::swap, просто добавим using в функцию:
template void zoo(T & one, T & two) { using std::swap;//Обеспечит вызов std::swap для типов swap(one, two);//для которых не предоставлен более подходящий swap }
Теперь, благодаря ADL для типов для которых предусмотрен собственный swap будет использоваться именно он, а для других - стандартный.
//... zoo(f, f);
int a = 10; int b = 20; zoo(a, b); std::cout << a << " " << b << std::endl;
Полный код примера:
#include

namespace First { struct Type {};
void foo(Type) { std::cout << "First foo" << std::endl; }
void bar(First::Type, First::Type) { std::cout << "First::bar" << std::endl; }
void swap(Type &, Type &) { std::cout << "First::swap" << std::endl; } }
namespace Second { struct Type {};
void foo(Type) { std::cout << "Second foo" << std::endl; }
void bar(First::Type, Second::Type) { std::cout << "Second::bar" << std::endl; }
void swap(Type &, Type &) { std::cout << "Second::swap" << std::endl; } }
template void zoo(T & one, T & two) { using std::swap; swap(one, two); }
int main() { First::Type f; Second::Type s;
foo(f); foo(s); bar(f, s);
zoo(f, f); zoo(s, s);
int a = 10; int b = 20; zoo(a, b); std::cout << a << " " << b << std::endl; }
http://rextester.com/LWCQY38186

Комментариев нет:

Отправить комментарий