Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
906
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Как перегрузить std::swap()
std::swap() используется многими контейнерами std (такими как std::list и std::vector ) во время сортировки и даже назначения.
Но реализация std swap() является очень обобщенной и довольно неэффективной для пользовательских типов.
Таким образом, эффективность может быть получена путем перегрузки std::swap() с помощью специальной реализации пользовательского типа. Но как вы можете реализовать его так, чтобы он был использован контейнерами std?
Правильный способ перегрузить swap-это записать его в том же пространстве имен, что и то, что вы меняете, чтобы его можно было найти с помощью зависящего от аргумента поиска (ADL). Одна особенно простая вещь, которую можно сделать, это:
class X
{
// ...
friend void swap(X& a, X& b)
{
using std::swap; // bring in swap for built-in types
swap(a.base1, b.base1);
swap(a.base2, b.base2);
// ...
swap(a.member1, b.member1);
swap(a.member2, b.member2);
// ...
}
};
Внимание Mozza314
Вот моделирование эффектов универсального std::algorithm , вызывающего std::swap, и заставляющего пользователя предоставить свою подкачку в пространстве имен std. Поскольку это эксперимент, то в данном моделировании используется namespace exp вместо namespace std .
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
exp::swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
namespace exp
{
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Для меня это распечатки:
generic exp::swap
Если ваш компилятор выводит что-то другое, то он неправильно реализует "two-phase lookup" для шаблонов.
Если ваш компилятор соответствует (любому из C++98/03/11), то он выдаст тот же вывод, который я показываю. И в этом случае произойдет именно то, чего вы боитесь. И помещение вашего swap в пространство имен std ( exp) не помешало этому случиться.
Мы с Дейвом оба являемся членами комитета и работаем в этой области стандарта уже десять лет (и не всегда в согласии друг с другом). Но этот вопрос давно решен, и мы оба согласны с тем, как он был решен. Игнорируйте экспертное мнение Дэйва/ответ в этой области на свой страх и риск.
Эта проблема возникла после публикации C++98. Примерно с 2001 года мы с Дейвом начали работать в этой области . И это самое современное решение:
// simulate <algorithm>
#include <cstdio>
namespace exp
{
template <class T>
void
swap(T& x, T& y)
{
printf("generic exp::swap\n");
T tmp = x;
x = y;
y = tmp;
}
template <class T>
void algorithm(T* begin, T* end)
{
if (end-begin >= 2)
swap(begin[0], begin[1]);
}
}
// simulate user code which includes <algorithm>
struct A
{
};
void swap(A&, A&)
{
printf("swap(A, A)\n");
}
// exercise simulation
int main()
{
A a[2];
exp::algorithm(a, a+2);
}
Выход есть:
swap(A, A)
Обновление
Было сделано замечание, что:
namespace exp
{
template <>
void swap(A&, A&)
{
printf("exp::swap(A, A)\n");
}
}
работает! Так почему бы не использовать это?
Рассмотрим случай, когда ваш A является шаблоном класса:
// simulate user code which includes <algorithm>
template <class T>
struct A
{
};
namespace exp
{
template <class T>
void swap(A<T>&, A<T>&)
{
printf("exp::swap(A, A)\n");
}
}
// exercise simulation
int main()
{
A<int> a[2];
exp::algorithm(a, a+2);
}
Теперь это снова не работает. :-(
Таким образом, вы можете поместить swap в пространство имен std и заставить его работать. Но вам нужно будет не забыть поместить swap в пространство имен A для случая, когда у вас есть шаблон: A<T> . И поскольку оба случая будут работать, если вы поместите swap в пространство имен A, просто легче запомнить (и научить других), чтобы просто сделать это одним способом.
Вам не разрешается (по стандарту C++) перегружать std::swap, однако вам специально разрешено добавлять специализации шаблонов для ваших собственных типов в пространство имен std. E.g.
namespace std
{
template<>
void swap(my_type& lhs, my_type& rhs)
{
// ... blah
}
}
тогда использование в контейнерах std (и в любом другом месте)будет выбирать вашу специализацию вместо общей.
Также обратите внимание, что предоставление реализации базового класса swap недостаточно хорошо для ваших производных типов. E.g. если у вас есть
class Base
{
// ... stuff ...
}
class Derived : public Base
{
// ... stuff ...
}
namespace std
{
template<>
void swap(Base& lha, Base& rhs)
{
// ...
}
}
это будет работать для базовых классов, но если вы попытаетесь поменять местами два производных объекта, он будет использовать универсальную версию из std, потому что шаблонный обмен является точным совпадением (и это позволяет избежать проблемы только замены частей 'base' ваших производных объектов).
NOTE: я обновил это, чтобы удалить неправильные биты из моего последнего ответа. Д'о! (спасибо puetzk и j_random_hacker за указание на это)
Хотя это правильно, что обычно не следует добавлять материал в пространство имен std::, добавление специализации шаблона для пользовательских типов специально разрешено. Перегрузка функций не происходит. Это тонкое различие :-)
17.4.3.1/1 Это не определено для программы C++, чтобы добавить объявления или определения к пространству имен std или пространствам имен с пространством имен std если не указано иное указанный. Программа может добавить шаблон специализации для любого стандартную библиотеку шаблонов в пространстве имен std. Такая специализация (полная или частичная) стандартная библиотека приводит к неопределенным результатам поведение, если только объявление не зависит от определяемого пользователем имени внешняя связь и если специализация шаблона не соответствует стандартные требования к библиотеке для исходного шаблона.
Специализация std::swap будет выглядеть так:
namespace std
{
template<>
void swap(myspace::mytype& a, myspace::mytype& b) { ... }
}
Без бита template<> это была бы перегрузка, которая не определена, а не специализация, которая разрешена. @Wilka's предложенный подход изменения пространства имен по умолчанию может работать с пользовательским кодом (из-за поиска Кенига, предпочитающего версию без пространства имен), но это не гарантируется и фактически не предполагается (реализация STL должна использовать полностью квалифицированный std::swap).
Там есть что- нить на comp.lang.c++.moderated с длинной обсуждение темы. Однако большая часть из них связана с частичной специализацией (что в настоящее время не является хорошим способом сделать).