[Перевод] Методы расширения в С++
Несколько дней назад Бьёрн Страуструп опубликовал предложение N4174 комитету по стандартизации С++ названное «Call syntax: x.f (y) vs. f (x, y)». Вот вкратце его суть: объявить выражение x.f (y) (вызов для объекта х метода f с аргументом y) эквивалентным выражению f (x, y) (вызов функции f с аргументами x и y). Т.е.x.f (y) означает:
Попробовать вызвать x.f (y): если класс объекта х содержит метод f, который может принять аргумент y — используем этот метод. Если пункт №1 не удался — проверяем, существует ли функция f, которая может принять аргументы x и y. Если это так — используем её. Если не найдено ни того, ни другого — генерируем ошибку. f (x, y) означает ровно то же самое: Попробовать вызвать x.f (y): если класс объекта х содержит метод f, который может принять аргумент y — используем этот метод. Если пункт №1 не удался — проверяем, существует ли функция f, которая может принять аргументы x и y. Если это так — используем её. Если не найдено ни того, ни другого — генерируем ошибку. Таким образом мы получаем возможность писать методы расширения, о которых мечтали многие С++ программисты. Я считаю это предложение одним из самых важных в эволюции языка С++.Методы расширения в C#Чтобы лучше понять о чём мы говорим, давайте вспомним как методы расширения реализованы в С#.Метод расширения позволяет вам добавить функциональность к существующему типу без модификации оригинального типа или создания унаследованного типа (и без необходимости перекомпиляции модуля, содержащего оригинальный тип). Предположим, вы хотите добавить к классу строки метод, подсчитывающий количество слов в ней. Для этого вы можете написать метод WordCount выглядящий вот так (для простоты будем считать разделителем слов один лишь символ пробела):
static class StringUtilities { public static int WordCount (string text) { return text.Split (new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Length; } } Теперь вы можете использовать его вот так:
var text = «This is an example»; var count = text.WordCount ();
Эквивалентность WordCount (text) и text.WordCount () это именно то, о чём говорит Страуструп в документе N4174.Обратите внимание, что методы расширения в С# имеют несколько ограничений:
метод расширения всегда должен быть объявлен как public static метод статического класса метод расширения имеет доступ полько к public методам и свойствам расширяемого типа Методы расширения в С++ Вопрос, который кто-то может задать: «Какие преимущества может дать эквивалентность x.f (y) и f (x, y) для языка?». Простой ответ: это даёт возможность определять методы расширения и использовать их без изменения уже существующего кода.Давайте посмотрим реальный пример. Стандартные контейнеры в С++ предоставляют метод find (), позволяющий найти определённый элемент. Но метод find () возвращает итератор и вам необходимо проверять его на равенство end () для понимания того, был элемент найден или нет. В то же время часто нам нужно не найти сам элемент, а проверить, содержится ли он в контейнере или нет. В стандартных контейнерах нет метода contains (), но мы можем написать вот такую функцию:
template
auto m = std: map
if (m.contains (1)) { } В случае когда x.f (y) и f (x, y) эквиваленты — вышеуказанный код абсолютно валиден (и красив).
Вот второй пример. Допустим вы хотите определить некоторые операторы, аналогичные имеющимся в LINQ под .NET. Вот примерная (упрощенная) реализация некоторых таких операторов для std: vector.
template
auto v = std: vector
auto s = sum (select (where (v, [](int e){return e % 2 == 0; }), [](int e){return e*e; })); Но этот код нравится мне ещё меньше. Во-первых, его тяжело читать (слишком много операций в одной строке и даже другое форматирование не очень помогает). Во-вторых, мы видим операции в инвертированном порядке относительно того, как они выполняются: сначала мы видим вызов sum, затем select и лишь потом where. Понять где заканчиваются аргументы одной функции и начинаются аргументы второй тоже не очень удобно.
Однако если стандарт языка определит эквивалентность x.f (y) и f (x, y), будет очень просто написать вот такой код:
auto v = std: vector
Вывод Документ N4174 пока что похож скорее на исследование теоретических возможностей, чем на формальный стандарт. Есть много разных аспектов, которые должны быть внимательно рассмотрены. Если вам интересно — почитайте документ сами. Тем ни менее, фича выглядит бесспорно полезной и я надеюсь настанет день, когда она войдёт в стандарт языка.