В прекрасном ответе на идиому copy-and-swap- a есть фрагмент кода, который мне нужно немного помочь:
class dumb_array
{
public:
// ...
friend void swap(dumb_array& first, dumb_array& second) // nothrow
{
using std::swap;
swap(first.mSize, second.mSize);
swap(first.mArray, second.mArray);
}
// ...
};
и он добавляет записку
Существуют и другие утверждения, что мы должны специализировать std :: swap для нашего типа, обеспечить обмен в классе вместе со свободным обменом функций и т. Д. Но это все не нужно: любое правильное использование swap будет осуществляться через неквалифицированный вызов и наша функция будет найдена через ADL. Одна функция будет делать.
С friend
я немного на «недружественных» терминах, я должен признать. Итак, мои основные вопросы:
- выглядит как свободная функция , но внутри тела класса?
- почему это не
swap
статично ? Очевидно, он не использует никаких переменных-членов. - «Любое правильное использование свопа будет определять своп через ADL» ? ADL будет искать пространства имен, верно? Но это также смотрит на классы? Или здесь, где
friend
приходит?
Побочные вопросы:
- В C ++ 11 я должен пометить свой
swap
snoexcept
? - С C ++ 11 и его диапазон-для , я должен поместить
friend iter begin()
и такfriend iter end()
же внутри класса? Я думаю, чтоfriend
здесь не нужно, верно?
c++
c++11
friend
copy-and-swap
towi
источник
источник
friend
функция вообще не является функцией-членом.Ответы:
Есть несколько способов написать
swap
, некоторые лучше, чем другие. Со временем, однако, было найдено, что единственное определение работает лучше всего. Давайте рассмотрим, как мы можем подумать о написанииswap
функции.Сначала мы видим, что контейнеры, подобные,
std::vector<>
имеют функцию-член с одним аргументомswap
, такую как:Естественно, наш класс тоже должен, верно? Ну не совсем. В стандартной библиотеке есть все виды ненужных вещей , и член
swap
является одним из них. Зачем? Продолжим.Что мы должны сделать, это определить, что канонично, и что нужно сделать нашему классу , чтобы работать с ним. И канонический метод обмена с
std::swap
. Вот почему функции-члены бесполезны: они вообще не таковы, как мы должны менять местами, и не имеют никакого отношения к поведениюstd::swap
.Ну, тогда, чтобы сделать
std::swap
работу, мы должны предоставить (иstd::vector<>
должны были предоставить) специализациюstd::swap
, верно?Что ж, это, безусловно, сработает в этом случае, но у него есть явная проблема: специализация функций не может быть частичной. То есть мы не можем специализировать шаблонные классы с этим, только с конкретными экземплярами:
Этот метод работает иногда, но не всегда. Должен быть лучший способ.
Там есть! Мы можем использовать
friend
функцию и найти ее через ADL :Когда мы хотим что-то поменять, мы ассоциируем †
std::swap
и затем делаем неквалифицированный вызов:Что такое
friend
функция? Вокруг этой области есть путаница.До стандартизации C ++
friend
функции выполняли что-то, называемое «добавление имени друга», когда код вел себя так, как будто функция была написана в окружающем пространстве имен. Например, это были эквивалентные предварительные стандарты:Однако, когда ADL был изобретен, это было удалено.
friend
Функция может затем только быть найдена с помощью ADL; если вы хотите, чтобы это была свободная функция, ее нужно было объявить так ( см. , например). Но вот! Была проблема.Если вы просто используете
std::swap(x, y)
, ваша перегрузка никогда не будет найдена, потому что вы явно сказали «смотритеstd
, и больше никуда»! Вот почему некоторые люди предлагают написать две функции: одну как функцию, которую можно найти через ADL , а другую - для обработки явныхstd::
квалификаций.Но, как мы видели, это не может работать во всех случаях, и в результате мы получаем ужасный беспорядок. Вместо этого идиоматическая перестановка пошла другим путем: вместо того, чтобы делать работу классов для обеспечения
std::swap
, это работа подменщиков, чтобы убедиться, что они не используют квалифицированныеswap
, как описано выше. И это имеет тенденцию работать довольно хорошо, пока люди об этом знают. Но в этом-то и заключается проблема: необязательно использовать неквалифицированный вызов!Чтобы сделать это проще, некоторые библиотеки, такие как Boost, предоставили функцию
boost::swap
, которая просто выполняет неквалифицированный вызовswap
,std::swap
в качестве связанного пространства имен. Это помогает снова сделать все лаконичным, но это все еще облом.Обратите внимание, что в C ++ 11 нет изменений в поведении
std::swap
, которое, как я и другие ошибочно считали, имело бы место. Если вас это укусило, читайте здесь .Короче говоря: функция-член - это просто шум, специализация уродлива и неполна, но
friend
функция завершена и работает. И когда вы подкачки, либо использоватьboost::swap
или неквалифицированныйswap
сstd::swap
связаны.† Неформально имя связывается, если оно будет учитываться во время вызова функции. Для деталей, прочитайте §3.4.2. В этом случае
std::swap
обычно не рассматривается; но мы можем связать его (добавить к множеству перегрузок, рассматриваемых неквалифицированнымиswap
), что позволит его найти.источник
std::vector<std::string>().swap(someVecWithData);
, что невозможно соswap
свободной функцией, потому что оба аргумента передаются по неконстантной ссылке.operator=
,operator+
иoperator+=
, но очевидно , эти операторы на соответствующих классах принимаются / , как ожидается, существует для симметрии. То же самое касается членаswap
+ пространства именswap
по моему мнению.function<void(A*)> f; if(!f) { }
может потерпеть неудачу только потому, чтоA
объявляет,operator!
что принимает такf
же хорошо, какf
и своеoperator!
(маловероятно, но может случиться). Еслиfunction<>
автор подумал «о, у меня есть« оператор bool », почему я должен реализовывать« оператор! »? Это нарушило бы СУХОЙ!», Это было бы фатальным. Вам просто нужно иметьoperator!
реализованный дляA
иA
иметь конструктор дляfunction<...>
, и все пойдет не так, потому что оба кандидата потребуют определенных пользователем преобразований.Этот код эквивалентен ( почти во всех отношениях):
Функция друга, определенная внутри класса:
inline
Точные правила приведены в разделе
[class.friend]
(я цитирую пункты 6 и 7 черновика C ++ 0x):источник
swap
он виден только ADL. Он является членом окружающего пространства имен, но его имя не видно другим формам поиска имен. РЕДАКТИРОВАТЬ: я вижу, что @GMan снова был быстрее :) @Ben, это всегда было так в ISO C ++ :)friend
функции находятся только в ADL, и если они должны быть просто свободными функциями сfriend
доступом, они должны быть объявлены какfriend
внутри класса, так и как обычное объявление свободной функции вне класса. Вы можете увидеть эту необходимость в этом ответе , например.