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

165
19 августа 2018, 06:00

Почему этот код работает, если даже если мы не используем пространство имен std?

#include <string>
#include <utility>
#include <iostream>
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

Answer 1

Это так называемый 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<typename T>
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<typename T>
void zoo(T & one, T & two)
{
    swap(one, two);
}

Теперь swap будет искаться в текущем пространстве имен и в пространстве имен своих аргументов. Но здесь возникает новая проблема. Что делать с типами, для которых нет swap? А для них мы будем использовать std::swap, просто добавим using в функцию:

template<typename T>
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 <iostream>

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<typename T>
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

Answer 2

Как я понимаю, работает ADL (Argument-Dependent Lookup), выполняющий поиск в соответствующем string пространстве имен.

"По-моему, так" (с) Пух

Если не так - пусть гуру подправят...

READ ALSO
Принцип вызова разных функций, но с одинаковыми параметрами

Принцип вызова разных функций, но с одинаковыми параметрами

Делема состоит из следующего, к примеру существует 2 функции си кода, которые в свою очередь принимают одинаковые параметры, но выполняют...

146
Где применить абстрактный класс и его свойства?

Где применить абстрактный класс и его свойства?

Создаю приложение в Qt для работы с базой данныхПо заданию необходимо использованием абстрактных классов

188
Не отображается код [закрыт]

Не отображается код [закрыт]

Во время отладки во второй части кода, visual начал серьезно тормозить, а программа повела себя не так как ожидалосьЯ прервал выполнение, но код...

185
Функция strtod() считывает только целую часть вещественного числа

Функция strtod() считывает только целую часть вещественного числа

Использую inih для парсинга ini файлаОбнаружилось то, что функция INIReader::GetReal(

167