Pull to refresh
23
0

Пользователь

Send message
В зависимости от порядка применения
Порядок применения в общем случае фиксирован — с начала в конец. Требование на входные итераторы — InputIterator, это такая суровая штука, что когда вы переходите к следующему итератору, все предыдущие могут быть уже невалидны (хороший пример: итератор чтения из файла).
дополнительный код, который нужен только для склейки с STL.
Там кода то на 4 строчки. А ещё это даст возможность использовать range-for пользователям С++11.
Нашел эту функцию в стандарте, там даже примечание для неё есть (25.3.4p5):
Remarks: result may be equal to first in case of unary transform, or to first1 or first2 in case of binary transform.
result в стандарте — это предпоследний параметр функции (итератор, по которому пишется ответ).
Взяв таким варварским способом указатель мы этот assert обойдем и будем лезть в массив бесконтрольно.
Это не страшно, ведь у нас оба массива фиксированной длины Dim. Это, конечно, быдлокод, но только лишь потому, что мы вместо вызовов begin() и end() взяли и подставили их реализацию.
Вы неправильно истолковали написанное, так сказано, что функциональный объект (std::minus<int>() в нашем случае) не имеет права менять объекты, а не то, что отрезки не могут перекрываться. Объект, собственно, ничего и не меняет, просто вычитает одно число из другого и выдает результат.
итератор (который тоже придется писать)
Можно простые указатели возвращать, они вполне себе итераторы. Можно даже набыдлокодить и написать как-нибудь так:
std::transform(&lhs[0], &lhs[0] + Dim, &rhs[0], &lhs[0], std::minus<int>())
Тогда уж стоит ещё упомянуть, что помимо спецификатора noexcept, есть ещё и compile-time оператор c тем же именем, который принимает на вход выражение и возвращает false, если в составе этого выражения есть хоть один вызов функции без noexcept спецификатора, и true в противном случае (более подробно о правилах можно прочитать здесь):
void f() noexcept;
void g();

cout << noexcept(f()); // true
cout << noexcept(g()); // false
cout << noexcept(true ? f() : g()); // also false

Такая встроенная примитивная compile-time проверка на то, может при вычислении выражения броситься исключение или нет. Можно использовать в сочетании со спецификатором noexcept:
void use_f() noexcept( noexcept(f()) ) // noexcept(true)
void use_g() noexcept( noexcept(g()) ) // noexcept(false)
А при чем тут хаб C++, в посте о нем вроде ни слова
В C++ нету оператора сложения двух контейнеров и нет функции, которая складывает элементы одного контейнера, удовлетворяющие какому-то условию, в другой (в силу того, что работа с контейнерами в стандартной библиотеке построена через механизм итераторов). Если и то, и то реализовать, то получиться также коротко (разве что лямбды страшновато выглядят) и также неэффективно:
template<typename Pred>
std::vector<int> put_if(const std::vector<int>& vec, Pred pred)
{
    std::vector<int> result;
    std::copy_if(std::begin(vec), std::end(vec), std::back_inserter(result), pred);
    return result;
}

std::vector<int> operator+(const std::vector<int>& l, const std::vector<int>& r)
{
    std::vector<int> result(l);
    std::copy(std::begin(r), std::end(r), std::back_inserter(result));
    return result;
}

std::vector<int> qsort(const std::vector<int>& vec)
{
    if (vec.size() <= 1)
        return vec;
    auto small = put_if(vec, [&](int x){ return x < vec[0]; });
    auto middle = put_if(vec, [&](int x){ return x == vec[0]; });
    auto large = put_if(vec, [&](int x){ return x > vec[0]; });
    return qsort(small) + middle + qsort(large);
}
Да и вообще — покажите решение любой задачи, которое будет на имперетивном языке проще и понятнее, чем на Haskell'е.
Напишите очередь. Только, пожалуйста, так, чтобы pop и push стоили O(1), а не O(n). Ну и использовать стандартную библиотеку, где она наверняка реализована, тоже нельзя, конечно.
На ВМК МГУ первый месяц вообще на бумажке пишут программы для машины Тьюринга и нормальные алгоритмы Маркова.
А не подскажите человеку, совсем не понимающему Хаскелл, где и как в этом коде элемент-разделитель выбирается?
Ваш трюк с циклом for мне кажется неуместным. Он хорош, когда действительно важно бежать с конца в начало, но, поскольку в ваших функциях вам все равно, то лучше использовать классический
for (size_t i=0; i<Dim; ++i)
Экономить 3 символа это, конечно, хорошо, но в отличие от классического варианта этот при прочтении прямо таки просит взять в руки бумажку и проверить, что тут нигде не налажали. Кроме того, отказ от него позволил бы вам сэкономить целый абзац в статье, раз уж вам это так нравиться.

Кроме того, вносить действие в шапку цикла считается признаком плохого стиля, а когда тело цикла пустое, рекомендуется вместо незаметной ; на той же строке, что и шапка, писать так:
for (size_t i=len; i--; ret[i]=v[i]) {}
или так:
for (size_t i=len; i--; ret[i]=v[i])
    ;

Все-таки это материал для новичков, поэтому надо стремиться показывать хороший код.

На место, откуда цитата из стандарта? 23.3.6.5p1
У вас же ещё и исключение наверх выбросилось, если вы его поймав, спокойно продолжаете с массивом работать, то это ваши проблемы. Главное, что вектор будет находиться в состоянии, достаточном для вызова его деструктора (хотя стандарт даже этого не гарантирует). Всё-таки гораздо чаще нужно не strong exception safety, а просто кинуть исключение, чтобы оно там где-то снизу по стеку поймалось. Плюс, если исключение кидается при добавлении одного элемента в конец массива и не происходит реаллокация, то мы вполне можем откатиться в предыдущее состоянии (хотя, опять же, стандарт не говорит, что это произойдет), а на этапе компиляции этого не выяснить.

Ну и выдачу ошибки компиляции в этом случае можно самому прикрутить к вектору строчек в 20 кода, а наоборот было бы сделать проблематично.
но вектор портится напрочь
Ну уж не напрочь, вектор останется старой длины, просто часть его элементов будет смувлена. C ним даже работать дальше можно (добавлять/удалять элементы и т.д.). Хотя стандарт ничего этого не гарантирует:
Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable T, the effects are unspecified.

интересно как авторы стандарта это мотивируют?
А что делать то предлагаете, если копирующего конструктора нету, а перемещающий не noexcept? Ошибку компиляции выдавать?
В этом случае точный тип derefUPLess известен только компилятору, его просто невозможно сохранить в переменной не используя auto. Конечно возможно написать так:
Если лямбда ничего не захватывает (квадратные скобочки пустые), то её можно преобразовать в указатель на функцию:
bool (*derefUPLess)(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&) =
    [](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2)
        { return *p1 < *p2; };

Выход простой, если в классе Widget перемещающий оператор присвоения продекларирован как noexcept, обьекты будут перемещаться, если нет — копироваться.
А если у Widget перемещающий конструктор не noexcept, но копирующего конструктора нет, то все равно перемещаться. Эту функциональность реализует std::move_if_noexcept()
Там записан алгоритм перехода от перестановки к следующей за ней в лексикографическом порядке. Суть: «Пусть в массиве числами от 1 до N записана перестановка. Найдем первый с конца элемента, который меньше следующего за ним. Затем в хвосте массива, следующем за найденным элементом, найдем минимальное число, большее чем этот элемент, переставим их местами и отсортируем хвост по возрастанию. Полученная перестановка и будет ответом.»
Из всех книг по программированию, которые я пытался читать в переводе на русский, буквально несколько переводов были действительны хороши. В остальных либо куча ошибок, либо очень тяжело читается, потому что переводчики не смогли сохранить стиль автора и текст превратился в какое-то нагромождение терминов.
Более того, по сути выражение std::iterator_traits<It>::value_type — не более чем гениальный костыль, придуманный на заре STL для определения типа получающегося при разыменовании итератора, первый вариант будет работать только с типом для которого определена специализация iterator_traits<>, а вот для второго нужен лишь operator*().
Немного неточно. Во-первых, при разыменовании итератора получается ссылочный тип (T&), а value_type — это тип с отброшенной ссылкой (T). Во-вторых, у std::iterator_traits<It> есть реализация по умолчанию, в которой std::iterator_traits<It>::value_type равен It::value_type. А нужен этот вспомогательный класс затем, что у обычного указателя (T*) не может члена-типа данных value_type, поэтому стандартная библиотека предоставляет для указателей специализацию данного класса.

Эта коллизия может произойти с любым кодом уже сейчас, по правилам С++11
std::vector(10, 20) создает обьект из 10 элементов, тогда как
std::vector{10, 20} создает обьект только из двух элементов.
Этот пример вроде неплохо демонстрирует, почему оно так работает. Проблема инициализации через фигурные скобочки в том, что она делает два дела одновременно: предоставляет синтаксис для инициализации с помощью std::initializer_list и работает как универсальная инициализация с контролем над сужением, которая может быть использована в любом месте, где синтаксисом языка допускается инициализация.

В C++98 для создания такой конструкции MyAllocList пришлось бы обьявить шаблонной структурой, продекларировать тип внутри нее и использовать вот так:
MyAllocList<Widget>::type lw;

Надо учитывать, что такой подход дает большую гибкость, потому что шаблон через using нельзя специализировать. Если брать пример с iterator_traits, то мы сможем написать так:
template<typename It>
using iterator_value_type = It::value_type;
но не сможем потом его специализировать, чтобы он работал и для обычных указателей.

default — этот модификатор заставляет компилятор генерировать автоматические функции класса
Тут есть один неприятный момент: если компилятор не cможет эту функцию сгенерировать, то он не выдаст вам сообщение об ошибке, а просто втихушку не станет её генерировать. Скажем вот такой код отлично компилируется:
struct T {
    std::unique_ptr<int> p; // unique_ptr не имеет конструктора копирования
    T(const T&) = default;  // поэтому компилятор не может сгенерировать этот конструктор для T
};
а ошибку вы получите, только когда попытаетесь данный конструктор использовать.

Однако один нюанс все-таки существует — decltype возвращает ссылку для всех выражений отличных от просто имени
Это неправда, например:
std::string f();
decltype( f() ) s; // s будет иметь тип std::string

decltype работает так: если ей передать имя переменной или выражение доступа к члену данных (например, a->b.c) без скобочек, то тип будет совпадать с типом переменной или члена данных. Иначе это считается выражением, а любое выражение имеет свой нессылочный тип (назовем его T) и value category (не знаю, как это правильно перевести). В соответствии с value category добавляется ссылочность: для lvalue результат будет T&, для xvalue — T&&, для prvalue — T.

Information

Rating
Does not participate
Location
Ижевск, Удмуртия, Россия
Registered
Activity