Существуют ли какие-либо препятствия в языке C ++, которые не позволяют использовать диапазоны D?

Это перекрестный вопрос C ++ / D. В языке программирования D есть диапазоны, которые - в отличие от библиотек C ++, таких как Boost.Range - не основаны на парах итераторов. Официальная группа изучения диапазонов C ++, похоже, увязла в разработке технической спецификации.

Вопрос: есть ли в текущем C ++ 11 или готовящемся к выпуску стандарте C ++ 14 какие-либо препятствия, препятствующие принятию диапазонов D, а также оптовой версии <algorithm>- с соответствующим диапазоном?

Я недостаточно хорошо знаю D или его диапазоны, но они кажутся ленивыми и компонуемыми, а также способными предоставить расширенный набор алгоритмов STL. Учитывая их претензии на успех D, было бы неплохо иметь библиотеку для C ++. Интересно, насколько важны уникальные особенности D (например, строковые миксины, унифицированный синтаксис вызова функций) для реализации его диапазонов, и мог ли C ++ имитировать это без особых усилий (например, C ++ 14 constexpr кажется очень похожим на оценку функции времени компиляции D )

Примечание. Я ищу технические ответы, а не мнения о том, являются ли диапазоны D правильным дизайном для библиотеки C ++.


person TemplateRex    schedule 31.08.2013    source источник
comment
Какая функция отсутствует? Помните, что конечный итератор - это просто объект, который по сравнению с другим итератором сообщает вам, что вы закончили. Диапазоны, основанные на ленивых итераторах C ++, несложны. Для причудливых ситуаций, таких как ленивые диапазоны, необходим дополнительный шаблон: для простых ситуаций, таких как диапазон по сжатой непрерывной памяти, я не могу представить, что диапазоны стиля D могут быть такими же быстрыми.   -  person Yakk - Adam Nevraumont    schedule 31.08.2013
comment
Вы смотрели на итераторы ускоренного преобразования и тому подобное? В частности, дайте конкретную задачу, которая дёшево в D, но дорого / невозможно в C ++.   -  person Yakk - Adam Nevraumont    schedule 31.08.2013
comment
Вы верите в это, потому что почему? Я считаю, что вы ошибаетесь или используете слова незнакомым мне образом. В любом случае, конкретная проблема, пожалуйста.   -  person Yakk - Adam Nevraumont    schedule 31.08.2013
comment
@Yakk Декартово произведение N диапазонов, лениво, что означает, что он возвращает диапазон N-кортежей, а приращение выполняет вычисление следующего элемента   -  person TemplateRex    schedule 31.08.2013
comment
@TemplateRex, что довольно просто сделать с итераторами C ++. Вы действительно думаете, что итераторы C ++ не могут этого сделать? Есть несколько свободных вариантов: кончается ли когда-нибудь, кончается все? Являются ли возвращенные элементы элементами или необязательно отсутствуют? Здесь легко сделать любой разумный выбор. Ядро - это простой набор итераторов из n, хранящийся в фасаде итераторов, который возвращает набор из n ссылок. Он будет таким же ленивым, как и его компоненты. Следующая конкретная проблема?   -  person Yakk - Adam Nevraumont    schedule 01.09.2013
comment
ideone.com/ZRDp4r требуется еще 30 строк _ 1_, а также тестирование и доработку для обеспечения производственного качества. Но я сомневаюсь, что ответ на конкретную задачу меня удовлетворит!   -  person Yakk - Adam Nevraumont    schedule 01.09.2013
comment
@Yakk хороший код! Я думаю, что меня беспокоит то, что этот стиль кода (а также Boost.Iterator) тесно связывает алгоритм (декартово произведение, преобразование, фильтр, zip и т. Д.) С итерацией. Я хотел бы увидеть общий ленивый итератор, который хранит алгоритмическую часть внутри объекта функции, который будет вызываться operator++.   -  person TemplateRex    schedule 01.09.2013
comment
@TemplateRex: function_input_iterator хранит функцию (объект), которая вызывается при увеличении. Теперь вам просто нужно отправиться в город с Boost.Coroutine для части алгоритма, но это означает, что вы привязываете алгоритм к самому диапазону.   -  person Xeo    schedule 01.09.2013
comment
@Yakk После дальнейшего изучения вашего кода, я должен признать, что адаптеры диапазона Boost / пользовательские итераторы довольно выразительны. Диапазоны стиля D без итераторов по-прежнему имеют интуитивно понятную привлекательность, но вы можете быть правы в том, что они не нужны для получения ленивых / составных алгоритмов. В любом случае, это выходило за рамки моего вопроса, но спасибо, что показали декартовый код продукта!   -  person TemplateRex    schedule 02.09.2013


Ответы (2)


Я не думаю, что существует какое-либо неотъемлемое техническое ограничение в C ++, которое сделало бы невозможным определение системы диапазонов D-стиля и соответствующих алгоритмов в C ++. Самая большая проблема языкового уровня будет заключаться в том, что for-циклы C ++ на основе диапазонов требуют, чтобы begin() и end() могли использоваться в диапазонах, но предполагая, что мы перейдем к определению библиотеки с использованием диапазонов стиля D, расширяя for- на основе диапазонов. петли, чтобы справиться с ними, кажется незначительным изменением.

Основная техническая проблема, с которой я столкнулся при экспериментировании с алгоритмами на диапазонах D-стиля в C ++, заключалась в том, что я не мог сделать алгоритмы такими же быстрыми, как мои реализации на основе итератора (фактически, курсора). Конечно, это могут быть просто реализации моего алгоритма, но я не видел никого, кто предлагал бы разумный набор алгоритмов на основе диапазона D в C ++, против которого я мог бы профилировать. Производительность важна, и стандартная библиотека C ++ должна обеспечивать, по крайней мере, слабоэффективные реализации алгоритмов (общая реализация алгоритма называется слабоэффективной, если она работает по крайней мере так же быстро при применении к структуре данных как пользовательская реализация того же алгоритма с использованием той же структуры данных с использованием того же языка программирования). Мне не удалось создать слабоэффективные алгоритмы на основе диапазонов D-стиля, и моя цель - на самом деле сильно эффективные алгоритмы (аналогичные слабоэффективным, но допускающие любой язык программирования и только предполагающие то же самое базовое оборудование).

Экспериментируя с алгоритмами на основе диапазона D-стиля, я обнаружил, что эти алгоритмы намного сложнее реализовать, чем алгоритмы на основе итераторов, и счел необходимым иметь дело с кладжами, чтобы обойти некоторые из их ограничений. Конечно, не все в том виде, как в настоящее время описываются алгоритмы в C ++, также идеально. Примерный план того, как я хочу изменить алгоритмы и абстракции, с которыми они работают, может быть STL 2.0 страница. Однако эта страница не особо касается диапазонов, поскольку это связанная, но несколько другая тема. Я бы предпочел представить диапазоны на основе итератора (ну, на самом деле, курсора), чем диапазоны в стиле D, но вопрос был не в этом.

Одна техническая проблема, с которой сталкиваются все абстракции диапазонов в C ++, - это разумный подход к временным объектам. Например, рассмотрим это выражение:

auto result = ranges::unique(ranges::sort(std::vector<int>{ read_integers() }));

В зависимости от того, являются ли ranges::sort() или ranges::unique() ленивыми или нет, необходимо иметь дело с представлением временного диапазона. Простое представление исходного диапазона не подходит ни для одного из этих алгоритмов, потому что временный объект исчезнет в конце выражения. Одна из возможностей может заключаться в перемещении диапазона, если он поступает как значение r, требуя разных результатов для ranges::sort() и ranges::unique(), чтобы различать случаи, когда фактический аргумент является либо временным объектом, либо объектом, поддерживаемым независимо. D не имеет этой конкретной проблемы, потому что он собирает мусор, и исходный диапазон, таким образом, будет оставаться в живых в любом случае.

В приведенном выше примере также показана одна из проблем с возможно ленивым вычисляемым алгоритмом: поскольку любой тип, включая типы, которые нельзя описать иначе, может быть выведен auto переменными или шаблонными функциями, нет ничего, что заставляло бы ленивое вычисление в конце выражения. Таким образом, результаты из шаблонов выражений могут быть получены, а алгоритм на самом деле не выполняется. То есть, если в алгоритм передается l-значение, необходимо убедиться, что выражение действительно оценивается для получения фактического эффекта. Например, любой sort() алгоритм, изменяющий всю последовательность, явно выполняет мутацию на месте (если вы хотите, чтобы версия не выполняла это на месте, просто скопируйте контейнер и примените версию на месте; если у вас есть только не- на месте, вы не можете избежать лишней последовательности, которая может быть непосредственной проблемой, например, для гигантских последовательностей). Предполагая, что это в некотором роде ленивый доступ к исходной последовательности с l-значением обеспечивает пик текущего статуса, что почти наверняка плохо. Это может означать, что ленивая оценка изменяющихся алгоритмов в любом случае не такая уж и хорошая идея.

В любом случае, есть некоторые аспекты C ++, которые делают невозможным немедленное принятие диапазонов D-sytle, хотя те же соображения применимы и к другим абстракциям диапазонов. Я думаю, что эти соображения, таким образом, также несколько выходят за рамки вопроса. Кроме того, очевидного «решения» первой из проблем (добавить сборку мусора) вряд ли получится. Я не знаю, есть ли решение второй проблемы в D. Может появиться решение второй проблемы (предварительно названное operator auto), но мне не известно о конкретном предложении или как на самом деле будет выглядеть такая функция.

Кстати, исследовательская группа диапазонов на самом деле не увязла в каких-либо технических деталях. До сих пор мы просто пытались выяснить, какие проблемы мы на самом деле пытаемся решить, и в некоторой степени охватить область решения. Кроме того, группы вообще не выполняют никакой работы! Фактическая работа всегда выполняется отдельными людьми, часто очень немногими людьми. Поскольку основная часть работы на самом деле заключается в разработке набора абстракций, я ожидал, что основы любых результатов группы по изучению диапазонов будут составлять от 1 до 3 человек, которые имеют некоторое представление о том, что необходимо и как это должно выглядеть.

person Dietmar Kühl    schedule 31.08.2013
comment
+1 за очень подробный ответ о вашем собственном опыте. На ум приходят два момента: 1) Я думал, что диапазоны D сохраняют ссылку на входной диапазон, а также хранят составной функциональный объект всех промежуточных алгоритмов, так что временной проблемы не существует. Или я что-то упускаю? 2) AFAIK, в D вызов result.front () или pop-front () будет оценивать сохраненный объект функции в текущем элементе ввода / находить следующий. - person TemplateRex; 01.09.2013
comment
@TemplateRex: О вашем первом пункте: я предполагаю, что диапазоны D сохраняют ссылку на оригинал и что нет проблем с временными диапазонами ввода в D, поскольку они хранятся достаточно долго. Однако в C ++ сделать то же самое нельзя, потому что даже если ссылка сохраняется на входной диапазон, объект будет уничтожен в конце выражения. В отличие от D, в C ++ нет сборки мусора. Однако вопрос, какие технические проблемы есть в C ++. Я не утверждаю, что такая же проблема существует в D. - person Dietmar Kühl; 01.09.2013
comment
Когда я пишу итераторы-генераторы, которые заранее не знают, заканчиваются ли они, мой конечный итератор - это просто пустой итератор с флагом, говорящим «Я омега»: вам не нужно искать конец, чтобы иметь конечный итератор. Поэтому я не уверен, почему петли, основанные на end() for, являются проблемой? - person Yakk - Adam Nevraumont; 01.09.2013
comment
@Yakk Проблема в том, что вам действительно нужны итераторы, а не конкретно end(). - person Xeo; 01.09.2013
comment
Не уверен, что временные файлы в D живут в куче (я был бы удивлен, если бы они были GC), но в C ++ вы бы сделали некоторые вещи, специфичные для rvalue, согласились. - person TemplateRex; 01.09.2013
comment
Проблема временных файлов существует и для диапазонов C ++. В Linq эквивалент (auto result = std::vector<int>{ read_integers() } | linq::orderby(linq::detail::identity_selector()) | linq::distinct;) является ошибкой компилятора, поскольку на самом деле нет хорошего решения к этому, кроме требования, чтобы пользователь передавал lvalue. Linq использует черту is_bindable_range, чтобы определить, является ли rvalue, но исключает диапазоны, в которых хранятся только итераторы (например, boost::iterator_range) - person Paul Fultz II; 01.09.2013
comment
@LucDanton: Проблема не столько в возврате, прошедшем через объекты, сколько в том, чтобы эффективно справляться с цеплянием за подходящие объекты, когда это необходимо, во внутренних объектах диапазона, используемых для ленивых вычислений. Также обратите внимание, что я не говорю, что работа с временными библиотеками невозможна, просто это соображение, которое необходимо учитывать при работе с диапазонами (по крайней мере, в C ++; я не я не знаю, изящно ли это решено в D), и я не думаю, что решение будет полностью тривиальным. - person Dietmar Kühl; 01.09.2013
comment
@ DietmarKühl относительно вашего документа STL 2.0, я думаю, вам следует отредактировать его для ясности, особенно введение. Каждый вводный абзац должен сопровождаться примером, который точно показывает, какая проблема существует. Прямо сейчас не совсем понятно, о чем вы говорите. Кроме того, предложения, которые начинаются со слова «Подход к ...», следует читать «Я предлагаю, чтобы ...» было ясно, что вы предлагаете решение, а не какое-либо другое значение (например, рассказывая нам, как другие люди работают над проблемой. ). - person Qwertie; 22.10.2013
comment
@Qwertie: Я знаю, что над страницей нужно много доработать. Я работаю над написанием подходящих предложений для исследовательской группы диапазонов, но они еще не совсем готовы. Каждый из них предназначен для определенного изменения (наиболее прогрессивное - это использование неоднородных типов для начала и конца однопроходных или многопроходных последовательностей). - person Dietmar Kühl; 23.10.2013
comment
Привет, ребята, мой собственный ограниченный опыт подсказывает, что диапазоны должны быть в основном такими же, как итераторы (диапазон - это единственный итератор, который достаточно умен, чтобы знать его конец). Следовательно, диапазон / итератор передается по значению, сохраняя ссылку на исходные данные. Затем временные блоки могут принадлежать диапазону. Вы можете спросить, что произойдет, если вы скопируете такой диапазон владения. Мое правило состоит в том, что диапазоны владения не могут быть скопированы, так как это поднимет вопрос, копировать ли базовый временный. Их нельзя скопировать, но можно перемещать и, следовательно, передавать таким функциям, как zip. - person Aaron McDaid; 16.08.2015
comment
... (продолжение), но я должен признать, что попытка выполнить сортировку на месте в таком временном диапазоне в целом не сработает. Так что, может, мне все-таки нечего добавить :( - person Aaron McDaid; 16.08.2015

Мои знания C ++ 11 гораздо более ограничены, чем я бы хотел, поэтому могут появиться новые функции, улучшающие вещи, о которых я еще не знаю, но есть три области, о которых я могу думать в данный момент. которые, по крайней мере, проблематичны: ограничения шаблона, static if и самоанализ типа.

В D функция на основе диапазона обычно имеет ограничение шаблона, указывающее, какой тип диапазонов она принимает (например, прямой диапазон или диапазон произвольного доступа). Например, вот упрощенная подпись для std.algorithm.sort:

auto sort(alias less = "a < b", Range)(Range r)
    if(isRandomAccessRange!Range &&
       hasSlicing!Range &&
       hasLength!Range)
{...}

Он проверяет, является ли переданный тип диапазоном произвольного доступа, что он может быть разрезан и имеет ли свойство length. Любой тип, который не удовлетворяет этим требованиям, не будет компилироваться с sort, и когда ограничение шаблона не работает, программисту становится ясно, почему его тип не будет работать с sort (вместо того, чтобы просто выдавать неприятную ошибку компилятора посередине. шаблонной функции, когда она не может скомпилироваться с заданным типом).

Теперь, хотя это может показаться просто улучшением удобства использования, чем просто выдача ошибки компиляции, когда sort не удается скомпилировать из-за того, что тип не имеет правильных операций, на самом деле это оказывает большое влияние на перегрузку функций, а также на самоанализ типа. Например, вот две перегрузки std.algorithm.find:

R find(alias pred = "a == b", R, E)(R haystack, E needle)
    if(isInputRange!R &&
       is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
{...}


R1 find(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle)
    if(isForwardRange!R1 && isForwardRange!R2 &&
       is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) &&
       !isRandomAccessRange!R1)
{...}

Первый принимает иглу, которая является только одним элементом, тогда как второй принимает иглу, которая является прямым диапазоном. Эти два могут иметь разные типы параметров, основанные исключительно на ограничениях шаблона, и могут иметь существенно разный внутренний код. Без чего-то вроде шаблонных ограничений у вас не может быть шаблонных функций, которые перегружены атрибутами своих аргументов (в отличие от того, что перегружены самими конкретными типами), что значительно усложняет (если не невозможно) иметь различные реализации на основе жанр используемого диапазона (например, входной диапазон или прямой диапазон) или другие атрибуты используемых типов. Некоторая работа была проделана в этой области в C ++ с концепциями и аналогичными идеями, но AFAIK, C ++ по-прежнему серьезно не хватает функций, необходимых для перегрузки шаблонов (будь то шаблонные функции или шаблонные типы) на основе атрибутов их типов аргументов, а чем специализация на определенных типах аргументов (как это происходит со специализацией шаблона).

Связанная функция будет static if. Это то же самое, что и if, за исключением того, что его состояние оценивается во время компиляции, и независимо от того, true или false будет фактически определять, какая ветвь скомпилирована, а не какая ветвь запущена. Это позволяет вам разветвлять код на основе условий, известных во время компиляции. например

static if(isDynamicArray!T)
{}
else
{}

or

static if(isRandomAccessRange!Range)
{}
else static if(isBidirectionalRange!Range)
{}
else static if(isForwardRange!Range)
{}
else static if(isInputRange!Range)
{}
else
    static assert(0, Range.stringof ~ " is not a valid range!");

static if может до некоторой степени устранить необходимость в ограничениях шаблона, поскольку вы можете по существу поместить перегрузки для шаблонной функции в одну функцию. например

R find(alias pred = "a == b", R, E)(R haystack, E needle)
{
    static if(isInputRange!R &&
       is(typeof(binaryFun!pred(haystack.front, needle)) : bool))
    {...}
    else static if(isForwardRange!R1 && isForwardRange!R2 &&
       is(typeof(binaryFun!pred(haystack.front, needle.front)) : bool) &&
       !isRandomAccessRange!R1)
    {...}
}

но это по-прежнему приводит к более неприятным ошибкам при сбое компиляции и фактически делает так, что вы не можете перегрузить шаблон (по крайней мере, с реализацией D), потому что перегрузка определяется до создания экземпляра шаблона. Итак, вы можете использовать static if для специализации частей реализации шаблона, но это не совсем дает вам достаточно того, какие ограничения шаблона заставляют вас не нуждаться в ограничениях шаблона (или что-то подобное).

Скорее static if отлично подходит для таких вещей, как специализация только части реализации вашей функции или для того, чтобы сделать так, чтобы тип диапазона мог правильно наследовать атрибуты типа диапазона, который он обертывает. Например, если вы вызываете std.algorithm.map для массива целых чисел, результирующий диапазон может иметь нарезка (потому что исходный диапазон есть), тогда как если вы вызвали map для диапазона, в котором не было нарезки (например, диапазоны, возвращаемые _ 22_ не может иметь срезы), то результирующие диапазоны не будут срезаны. Для этого map использует static if для компиляции в opSlice, только если исходный диапазон это поддерживает. В настоящее время код map, который делает это, выглядит так:

static if (hasSlicing!R)
{
    static if (is(typeof(_input[ulong.max .. ulong.max])))
        private alias opSlice_t = ulong;
    else
        private alias opSlice_t = uint;

    static if (hasLength!R)
    {
        auto opSlice(opSlice_t low, opSlice_t high)
        {
            return typeof(this)(_input[low .. high]);
        }
    }
    else static if (is(typeof(_input[opSlice_t.max .. $])))
    {
        struct DollarToken{}
        enum opDollar = DollarToken.init;
        auto opSlice(opSlice_t low, DollarToken)
        {
            return typeof(this)(_input[low .. $]);
        }

        auto opSlice(opSlice_t low, opSlice_t high)
        {
            return this[low .. $].take(high - low);
        }
    }
}

Это код в определении типа возвращаемого типа map, и то, скомпилирован ли этот код или нет, полностью зависит от результатов static if, ни один из которых не может быть заменен специализациями шаблонов, основанными на определенных типах, без необходимости писать новый специализированный шаблон для map для каждого нового типа, который вы используете с ним (что, очевидно, несостоятельно). Чтобы компилировать код, основанный на атрибутах типов, а не на конкретных типах, вам действительно нужно что-то вроде static if (чего в C ++ в настоящее время нет).

Третий важный элемент, которого не хватает C ++ (и который я более или менее затронул), - это самоанализ типов. Тот факт, что вы можете делать что-то вроде is(typeof(binaryFun!pred(haystack.front, needle)) : bool) или isForwardRange!Range, имеет решающее значение. Без возможности проверить, имеет ли конкретный тип определенный набор атрибутов или что конкретный фрагмент кода компилируется, вы даже не можете написать условия, которые используются в ограничениях и static if шаблонах. Например, std.range.isInputRange выглядит примерно так

template isInputRange(R)
{
    enum bool isInputRange = is(typeof(
    {
        R r = void;       // can define a range object
        if (r.empty) {}   // can test for empty
        r.popFront();     // can invoke popFront()
        auto h = r.front; // can get the front of the range
    }));
}

Он проверяет, компилируется ли конкретный фрагмент кода для данного типа. Если да, то этот тип можно использовать в качестве диапазона ввода. Если нет, значит, не может. AFAIK, на C ++ невозможно сделать что-либо даже отдаленно подобное. Но для разумной реализации диапазонов вам действительно нужно уметь делать такие вещи, как have isInputRange или проверять, компилируется ли конкретный тип с sort - is(typeof(sort(myRange))). Без этого вы не сможете специализировать реализации на основе того, какие типы операций поддерживает конкретный диапазон, вы не можете правильно пересылать атрибуты диапазона при его упаковке (а функции диапазона все время переносят свои аргументы в новые диапазоны) и вы даже не можете должным образом защитить свою функцию от компиляции с типами, которые с ней не работают. И, конечно же, результаты static if и ограничений шаблона также влияют на самоанализ типа (поскольку они влияют на то, что будет и что не будет компилироваться), поэтому эти три функции во многом взаимосвязаны.

На самом деле, основные причины, по которым диапазоны не очень хорошо работают в C ++, - это некоторые причины, по которым метапрограммирование в C ++ примитивно по сравнению с метапрограммированием в D. AFAIK, нет причин, по которым эти функции (или аналогичные) нельзя было добавить в C ++ и решить проблему, но до тех пор, пока в C ++ не появятся возможности метапрограммирования, аналогичные функциям D, диапазоны в C ++ будут серьезно нарушены.

Другие функции, такие как миксины и унифицированный синтаксис вызова функций, также могут помочь, но они далеко не такие фундаментальные. Миксины помогли бы в первую очередь уменьшить дублирование кода, а UFCS помогает в первую очередь сделать так, чтобы общий код мог просто вызывать все функции, как если бы они были функциями-членами, так что если тип определяет конкретную функцию (например, find), тогда это будет используется вместо более общей, бесплатной версии функции (и код по-прежнему работает, если такая функция-член не объявлена, потому что тогда используется бесплатная функция). UFCS принципиально не требуется, и вы даже можете пойти в противоположном направлении и отдать предпочтение бесплатным функциям для всего (как C ++ 11 сделал с begin и end), хотя для этого по сути требуется, чтобы бесплатные функции могли тестировать для существования функции-члена, а затем вызовите функцию-член изнутри, а не используя свои собственные реализации. Итак, вам снова понадобится самоанализ типа вместе с static if и / или ограничениями шаблона.

Как бы мне ни нравились диапазоны, на данный момент я в значительной степени отказался от попыток что-либо сделать с ними на C ++, потому что функций, которые делают их разумными, просто нет. Но если другие люди смогут понять, как это сделать, тем больше у них сил. Тем не менее, независимо от диапазонов, я бы хотел, чтобы C ++ получил такие функции, как ограничения шаблона, static if и самоанализ типов, потому что без них метапрограммирование способом менее приятным, до такой степени, что пока я это делаю все время в D, я почти никогда не делаю этого в C ++.

person Jonathan M Davis    schedule 31.08.2013
comment
Собственно, ничто из вышеперечисленного не является препятствием для использования диапазонов в C ++! Хотя технология, используемая для реализации аналогичных функций в C ++, может быть более сложной, они действительно работают и используются. - person Dietmar Kühl; 01.09.2013
comment
+1 за очень подробный ответ, который показывает, какие функции D важны для его диапазонов / алгоритмов. Однако, учитывая ваше объяснение, я не уверен, не мог ли C ++ соответствовать интерфейсу диапазона D. Конечно, sfinae более подробный, чем static if, но действительно ли он делает невозможным копирование некоторых действительных функций D? И предстоящие Concepts Lite должны точно соответствовать вашей IsInputRange группе допустимых выражений. - person TemplateRex; 01.09.2013
comment
@ DietmarKühl Не могли бы вы превратить свой комментарий в ответ с точки зрения C ++? Я понимаю, что эти межъязыковые вопросы сложны. - person TemplateRex; 01.09.2013
comment
Я должен согласиться с Дитмаром Кюлем. Я сам реализовал диапазоны в стиле D, и метапрограммирование не было проблемой вообще. (Хотя я нахожусь в лагере, который предпочел бы продолжать использовать кладжи, которые у нас есть прямо сейчас, ожидая концепций, а не вносить static if и тому подобное в язык.) У меня действительно есть некоторые проблемы с дублированием кода, так что это действительно бросается в глаза . К сожалению, я недостаточно знаю о миксинах D, чтобы сказать, помогут они или нет. - person Luc Danton; 01.09.2013
comment
Что ж, если кто-то придумал, как обойти отсутствие ограничений шаблона, static if, и самоанализ типа в C ++ и реализовать полезные диапазоны в C ++, тем больше для него возможностей. У меня явно недостаточно хорошего метапрограммирования C ++ для этого. Но я был бы очень удивлен, если бы ваш средний программист на C ++ мог написать функцию на основе диапазона, и если диапазон и функции на основе диапазона не могут быть написаны вашим средним программистом, я серьезно сомневаюсь, что у них много жизнеспособность. Метапрограммирование на языке D действительно доступно среднему программисту. - person Jonathan M Davis; 01.09.2013
comment
Рекордные Boost.TypeTraits и Boost.CallTraits были выпущены в 2000 году, boost::enable_if в 2004 году. Я также сомневаюсь, что они были первыми подобными попытками. - person Luc Danton; 01.09.2013