kzen.dev
  • Вопросы
  • Метки
  • Пользователи
Оповещения
Вознаграждения
Регистрация
После регистрации, сможете получать уведомления об ответах и комментариях на Ваши вопросы.
Вход
Если у Вас уже есть аккаунт, войдите чтобы проверить новые уведомления.
Тут будут вознаграждения за добавленные вопросы, ответы и комментарий.
Дополнительно
Источник
Редактировать
 Adam
Adam
Вопрос

Как перегрузить std::swap()

std::swap() используется многими контейнерами std (такими как std::list и std::vector) во время сортировки и даже присвоения.

Но std-реализация swap() очень обобщена и довольно неэффективна для пользовательских типов.

Таким образом, эффективность может быть достигнута путем перегрузки std::swap() с реализацией, специфичной для пользовательских типов. Но как реализовать ее так, чтобы она использовалась контейнерами std?

112 2008-08-14T19:24:17+00:00 4
 sbi
sbi
Редактировал вопрос 22-го марта 2013 в 6:30
Программирование
c++
performance
optimization
stl
c++-faq
Решение / Ответ
Dave Abrahams
Dave Abrahams
21-го апреля 2010 в 4:02
2010-04-21T16:02:22+00:00
Дополнительно
Источник
Редактировать
#8415298

Правильный способ перегрузить swap - записать его в том же пространстве имен, что и то, что вы меняете, чтобы его можно было найти через argument-dependent lookup (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);
        // ...
    }
};
 Community
Community
Редактировал ответ 13-го февраля 2019 в 5:57
127
0
Howard Hinnant
Howard Hinnant
8-го декабря 2011 в 11:52
2011-12-08T23:52:13+00:00
Дополнительно
Источник
Редактировать
#8415301

Внимание 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

Если ваш компилятор печатает что-то другое, то он неправильно реализует «двухфазный поиск» для шаблонов.

Если ваш компилятор соответствует (любому из 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», просто легче запомнить (и научить других) просто сделать это одним способом.

Andrea Corbellini
Andrea Corbellini
Редактировал ответ 31-го августа 2015 в 5:13
68
0
 Wilka
Wilka
14-го августа 2008 в 7:46
2008-08-14T19:46:32+00:00
Дополнительно
Источник
Редактировать
#8415295

Вам не разрешается (по стандарту C++) перегружать std::swap, однако вам разрешено добавлять специализации шаблонов для ваших собственных типов в пространство имен std. Например.

namespace std
{
    template<>
    void swap(my_type& lhs, my_type& rhs)
    {
       // ... blah
    }
}

тогда использование в контейнерах std (и в любом другом месте) будет использовать вашу специализацию вместо общей.

Также обратите внимание, что реализация swap в базовом классе недостаточно хороша для ваших производных типов. Например, если у вас есть

class Base
{
    // ... stuff ...
}
class Derived : public Base
{
    // ... stuff ...
}

namespace std
{
    template<>
    void swap(Base& lha, Base& rhs)
    {
       // ...
    }
}

это будет работать для базовых классов, но если вы попытаетесь поменять местами два производных объекта, то будет использована общая версия из std, потому что шаблонизированный swap является точным совпадением (и это позволяет избежать проблемы замены только 'базовых' частей ваших производных объектов).

ПРИМЕЧАНИЕ: Я'обновил это, чтобы удалить неправильные биты из моего последнего ответа. D'oh! (спасибо puetzk и j_random_hacker за то, что указали на это).

Dan Nissenbaum
Dan Nissenbaum
Редактировал ответ 27-го января 2014 в 2:15
53
0
 puetzk
puetzk
20-го сентября 2008 в 10:24
2008-09-20T22:24:12+00:00
Дополнительно
Источник
Редактировать
#8415297

Хотя правильно, что обычно не следует добавлять что-либо в пространство имен 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' подход изменения пространства имен по умолчанию может сработать в пользовательском коде (из-за того, что Koenig lookup предпочтет версию без пространства имен), но это' не гарантировано, и на самом деле не должно (реализация STL должна использовать полностью квалифицированное std::swap).

Существует тема на comp.lang.c++.moderated с длинным обсуждением этой темы. Большая часть из них посвящена частичной специализации, хотя (в настоящее время нет хорошего способа сделать это).

 puetzk
puetzk
Редактировал ответ 20-го сентября 2008 в 10:37
29
0
Похожие сообщества 9
pro.cxx
pro.cxx
5 992 пользователей
C/C++ chat 0. Простые вопросы, лабы и о IDE — в чат новичков @supapro 1. No Ads, offtop, flood 2. Полные правила тут https://t.me/ProCxx/259155 Объявления о вакансиях,эвентах - в лс @AlexFails или @MasterZiV
Открыть telegram
supapro.cxx
supapro.cxx
5 026 пользователей
Чат для тех, кто немного знает C++, простые вопросы по C++, синтаксису и ide – сюда, а для другого есть: /Главный чат по серьезным вопросам — @ProCxx /Чат-флудилка — @fludpac /прогерские вопросы – @pro_prog 🚫flood, pron, spam; ✅УВАЖАЙТЕ ДРУГ ДРУГА!
Открыть telegram
Хирьянов Т.Ф., Алгоритмы и структуры данных (С++)
Хирьянов Т.Ф., Алгоритмы и структуры данных (С++)
1 393 пользователей
Лекции: www.youtube.com/playlist?list=PLRDzFCPr95fL_5Xvnufpwj2uYZnZBBnsr Практика: cs.mipt.ru/cpp_algo Спонсировать: www.paypal.me/tkhirianov Онлайн компилятор https://godbolt.org
Открыть telegram
Чат конференции C++ Russia
Чат конференции C++ Russia
1 205 пользователей
Канал конференции: @cpprussia_channel Ближайшая конференция — C++ Russia 2023, 11–12 мая · Online Оффлайн — 23–24 мая · Москва Билеты: https://tinyurl.com/CPPRussia2023 Саппорт: @JUGConfSupport_bot
Открыть telegram
Android NDK (C++) — русскоговорящее сообщество
Android NDK (C++) — русскоговорящее сообщество
783 пользователей
Общаемся на темы, посвященным Android-разработке на C++. Обмен новостями, опытом и наработками. + Про Android: @android_ru + Про iOS: @ios_ru + Канал Android: @pandroidtoday_ru + Вакансии: @mobile_jobs Рекомендуем отключить уведомления.
Открыть telegram
C++ для самых маленьких и отчаяных
C++ для самых маленьких и отчаяных
564 пользователей
Лоу левел (по среднему IQ участников) чатик По продвижению вакансий писать @vertver Флудилка чата - @hckcxx
Открыть telegram
Добавить вопрос
Категории
Все
Технологий
Культура / Отдых
Жизнь / Искусство
Наука
Профессии
Бизнес
Пользователи
Все
Новые
Популярные
1
Ilya Smirnov
Зарегистрирован 6 дней назад
2
Денис Васьков
Зарегистрирован 1 неделю назад
3
Dima Patrushev
Зарегистрирован 1 неделю назад
4
sirojidddin otaboyev
Зарегистрирован 2 недели назад
5
Елена Гайдамамакинат
Зарегистрирован 2 недели назад
ID
KO
RU
© kzen.dev 2023
Источник
stackoverflow.com
под лицензией cc by-sa 3.0 с атрибуцией