• Корутины в C++20. Часть 1
    +1
    Корутины в C++20 асимметричные [...]

    Симметричные. Вроде как в последний момент подправили:
    C++ Coroutines: Understanding Symmetric Transfer
    Symmetric transfer and no-op coroutines

  • Мой подход к реализации делегатов в C++: вызов функции с неизвестными параметрами во время выполнения
    +2

    Отталкиваясь от примера, не вижу где здесь делегаты. Мне кажется, вы смешиваете несколько проблем в одну. Если вам просто нужно превратить массив any в tuple<>, так и сделайте. Имея такую основу, всё становится проще. Вот несколько строчек:


    #include <vector>
    #include <any>
    #include <tuple>
    
    template<typename... Args>
    struct Binder
    {
        static std::tuple<Args...> convert_args(std::vector<std::any> any_args)
        {
            auto convert = [i = 0u, &any_args](auto& arg) mutable
            {
                arg = std::any_cast<decltype(arg)>(any_args.at(i));
                ++i;
            };
            std::tuple<Args...> vs;
            std::apply([&](Args& ... args) { (convert(args), ...); }, vs);
            return vs;
        }
    
        template<typename F>
        decltype(auto) call(F f, std::vector<std::any> args) const
        {
            return std::apply(std::move(f), convert_args(std::move(args)));
        }
    };
    
    #include <string>
    #include <iostream>
    
    int f(int a, std::string s)
    {
        std::cout << "int: " << a << "\nstring: " << s << std::endl;
        return 1;
    }
    
    int main()
    {
        return Binder<int, std::string>().call(
            f, {5, std::string("Hello, Delegates!")});
    }
    
  • Simple MCerver — небольшая оболочка для сервера Minecraft
    0

    Ну зачем же вы вредный код показываете: virtual деструктор здесь не нужен. Да и вообще, реюзабельность таких темплейтных синглтон классов — сильно под сомнением — вам всё равно нужно еще дописать MyClass. Этот код компилируется [1]:


    MyClass instances[100];

    [1] https://wandbox.org/permlink/ede7bBfl0KSq6QV2

  • Тесты на Си без SMS и регистрации
    +3
    В плюсах нет возможности в рантайме посмотреть все ф-и с таким-то атрибутом. Даже предлагаемые пропозалы, по-моему, по статической рефлексии не включают в себя возможность в компайл тайме узнать атрибуты ф-и. Мне кажется, атрибуты также не влияют на name mangling, то есть по символам ф-й также не получится что-либо узнать.
    Без сторонней тулзы, которая парсит код, не получится. Или я что-то упустил?
  • Разрушительные исключения
    0

    Если подумать, я представляю только один случай когда это действительно нужно: когда инициализацию подобьектов нужно выполнить "всю за один раз". То есть по сути, конструкторы базовых классов пустые, а инициализация всего происходит в init(). Но, опять же, мне кажется это больше проблема дизайна класса и/или API. Даже с примером выше — все красиво можно передать в качестве аргументов конструктора базового класса, если ему нужны какие-то дополнительные данные.
    Я могу ошибаться, но не представляю случая где двухэтапной инициализации нельзя избежать.
    Может у вас есть конкретный пример ?

  • Разрушительные исключения
    +1

    В том контексте что привели вы, init() после new B() приведет к вызову все того-же B::init(), то есть всю инициализацию можно сделать в конструкторе B. Как по мне, так двухфазная инициализация — усложняет код и лучше ее избегать. То что я видел:


    1. Использование init() чтобы обработать ошибки создания объекта в случае когда эксепшины отключены.
      Иметь is_valid() — это хорошая альтернатива.
    2. Использование init() для настройки "зависимостей" объекта в том случае когда существуют циклические и другие проблемные зависимости объектов.
      Тут, к сожалению, нужно править код, если можно.
  • Разрушительные исключения
    +1

    Как по мне, всё равно — сомнительно: в случае активного эксепшина, вы все равно пишите в лог, то есть оригинальный эксепшн — теряется. Если на нем построена какая-то логика приложения (восстановки, повторов, прочее) — это логика также не будет выполнена. Если это нормально — то есть не делать какую-то обработку такого-то эксепшина — то почему-бы его и не выбрасывать совсем? Мне кажется, что у вас это имеет смысл из-за второй части статьи: все эксепшины — собираются вместе, ничего не игнорируется и потом все обрабатывается. Но, опять-же, при таком подходе — все деструкторы noexcept, то есть все что сказано в первой части статьи — не имеет смысла.


    По поводу:


    Когда очень-очень хочется выбросить сразу несколько исключений

    мне кажется или вы говорите об std::throw_with_nested и семействе [1] ?


    [1] nested_exception: https://en.cppreference.com/w/cpp/error/nested_exception

  • Бесполезный отложенный неблокирующий обмен сообщениями в MPI: лайт-аналитика и туториал для тех, кто немножечко «в теме»
    0

    Статью не читал, у вас тут память течёт:


    delete[] A, B, C, D, E, G, T0, T1, T2;
  • 2. По мотивам Мейерса «Эффективный и современный С++» — детали вывода типа шаблона для массивов
    0

    От себя добавлю, что, всё же, в вашем примере, plain C-версия тоже может быть аккуратненькой:


    arr[10];
    std::sort(std::begin(arr), std::end(arr));

    До 17х плюсов, единственный момент, где удобно использовать C-шные массивы — так это при объявлении "константных массивов", когда можно просто добавлять элементы в конец и не париться про размер, например:


    const struct
    {
        const int id;
        const char* const name;
    } k_info[]
    {
        {1, "id1"},
        {2, "id1"},
        {3, "id1"},
        {4, "id1"},
    };

    Позже, чтобы добавить новый элемент в такой массив, я просто иду и добавляю строчку в конец, не меняя в другом месте (пару строчек выше) размер массива в случае с std::array + с ним не получится использовать анонимную структуру. Но это мелочи.


    После 17тых плюсов, использовать std::array для таких случаев стало немного удобней:


    std::array data = {1, 2, 3, 4};

    работает на ура благодаря class template argument deduction

  • Используем template + constexpr для создания масок регистров периферии микроконтроллера на этапе компиляции (C++14)
    0
    Почему у вас все функции константные, даже те, которые меняют логическое состояние ?)
  • Повседневный C++: изолируем API в стиле C
    0
    я имел в виду то, что написал mickvav ниже
  • Повседневный C++: изолируем API в стиле C
    +1
    + логика странная (зачем открывать файл и сразу же его закрывать) + ф-я возвращала bool, судя по всему, но потом была переделка на исключения. В общем, пример неудачен да ещё и с ошибками
  • Как я писал компилятор С++. Пересказ спустя 15 лет
    0

    Круто. Спасибо за статью.
    Для тех, кто хочет посмотреть/подебажить всё вместе — держите CMakeLists.txt:


    CMakeLists.txt
    cmake_minimum_required(VERSION 3.4)
    
    project(nrcpp)
    
    # Remove unneeded configurations
    set(CMAKE_CONFIGURATION_TYPES "Debug;Release" CACHE STRING "" FORCE)
    
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267 /wd4244 /wd4996 /wd4018 /wd4477")
    
    file(GLOB kpp_src "KPP_1.1/*.cpp")
    file(GLOB kpp_hds "KPP_1.1/*.h")
    set(kpp_other_files 
        KPP_1.1/in.txt
        KPP_1.1/in.txt.comment
        KPP_1.1/in.txt.slash
        KPP_1.1/in.txt.trig
        KPP_1.1/limits.h.comment
        KPP_1.1/limits.h.slash
        KPP_1.1/limits.h.trig
        KPP_1.1/out.txt)
    
    add_library(nrcpp_lib nrcpp/LIB/NRC.H nrcpp/LIB/NRC.CPP)
    target_include_directories(nrcpp_lib PUBLIC nrcpp/LIB)
    
    add_executable(KPP ${kpp_src} ${kpp_hds} ${kpp_other_files})
    
    file(GLOB nrcpp_src "nrcpp/*.cpp")
    file(GLOB nrcpp_hds "nrcpp/*.h")
    set(nrcpp_other_files 
        nrcpp/in.txt
        nrcpp/out.txt)
    
    add_executable(nrcpp ${nrcpp_src} ${nrcpp_hds} ${nrcpp_other_files})
    target_link_libraries(nrcpp nrcpp_lib)

    я собирал как-то так:


    mkdir build && ^
    cd build && ^
    cmake -G "Visual Studio 15 2017 Win64" .. && ^
    cmake --build . --config Debug
  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    +3

    Отторжения нет. Мне уже нравится ^_^

  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    +1

    Спасибо.


    все параметры передаются по значению без копирования. То есть если вы передали что-то в другую функцию без &, больше вы его использовать не сможете.

    По сути, в плюсах — это аналог rvalue с мув семантикой. Но в плюсах нет поддержки овнершипа на уровне языка/компилятора:


    std::string str = "I'm the owner";
    foo(std::move(str)); // передали овнершип в функцию

    Как я уже писал Halt — смотрю Rust из-за этих манипуляций с лайфтаймом :)

  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    0

    Спасибо за объяснения.


    Структуры вида struct Pair(i32, f32); служат для другой цели. Их используют когда хочется связать несколько значений под одним общим именем типа, но отдельные поля именовать не хочется.

    По сути (очень грубо) это и есть тюплы. Просто это strong typedef (т.е., алиас, который кроме синонима вводит ещё и новый тип на основе которого можно делать перегрузку ф-й и прочее. То, чего не хватает плюсам на уровне языка).


    struct Meters(f32) — подтверждает это. На псевдо коде мы имеем что-то такого:


    void foo(f32); // 1
    void foo(Meters); // 2
    
    foo(f32{0}); // 1
    foo(Meters{0}); // 2

    (Подправте, если ошибаюсь)


    С макросами — понял — аналог шаблонов. Но тогда


    Идентификаторы с крючком были введены, чтобы отличать их от других шаблонных параметров и чтобы их можно было аккуратно прилепить к ссылке:

    есть и макросы (которые судя по комментариям ниже могут генерить код/модули) и есть шаблоны.


    Спасибо за объяснения. Я фан явного указания жизни обьектов. Из-за вас теперь уже точно смотрю на Rust :)

  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    +3

    Спасибо за комментарий.
    Предупреждаю — ниже взгляд человека, который НЕ разбирался в языке совершенно ни грамма.


    Навскидку:


    name: &'static str,

    этот маленький ' крючёчек для меня, как человека с плохим зрением — просто сложно заметен.
    Непривычно смотрится:


    &self, f: &mut Formatter

    но это просто нужно один раз понять (я не разбирался что всё это значит ещё. Догадываюсь, что, возможно, в Rust-е всё const-by-default, поэтому &mut. А & — потому что, так же, как и в плюсах, есть возможность передавать по ссылке/по значению. Но опять же — это просто догадки).


    Непривычно смотрится просто знак восклицания println! и всякие штуки, типа slice: &[i32].
    Дальше:


    // A tuple struct
    struct Pair(i32, f32);

    только с комментария догадываюсь, что это аналог алиаса (?): using Pair = std::tuple<i32, f32>; и, возможно, доступ будет происходить по индексам.


    Ну, а этот код из статьи — это вообще ад:


    macro_rules! __impl_slice_eq1 {
        ($Lhs: ty, $Rhs: ty) => {
            __impl_slice_eq1! { $Lhs, $Rhs, Sized }
        };
    
        ($Lhs: ty, $Rhs: ty, $Bound: ident) => {
            #[stable(feature = "rust1", since = "1.0.0")]
            impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> {
                #[inline]
                fn eq(&self, other: &$Rhs) -> bool { self[..] == other[..] }
                #[inline]
                fn ne(&self, other: &$Rhs) -> bool { self[..] != other[..] }
            }
        }
    }

    Опять же — уверен, что если разобраться — всё становиться логичным.
    Спасибо

  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    0

    Я настолько привык к C++, что жить без него не могу и уже не замечаю проблемы с синтаксисом )
    Спасибо за советы. Пойду смотреть Rust

  • Черная магия метапрограммирования: как работают макросы в Rust 1.15
    +1

    Не пытался садится за Rust всерьёз из-за синтаксиса. Он меня жутко пугает. Что посоветуете? Потерпеть сначала, а потом будет всё в порядке?

  • Работа с кортежами С++ (std::tuple). Функции foreach, map и call
    +1

    Не вы одни сдаётесь. Вон, Мейерс тоже сдался. Но это еще не означает, что C++ — уродливый. Вы на новые стандарты C — посмотрите. Там тоже можно за голову взяться. Это нормально для языков с историей, которые еще и развиваются

  • Работа с кортежами С++ (std::tuple). Функции foreach, map и call
    0

    да, дело вкуса, разници нет. Лично я думаю, что static_cast() — слишком длинно, C-style каст — слишком незаметно, а странный void() — и в глаза падает (идешь гуглить, что это за void()), и не слишком длинный

  • Работа с кортежами С++ (std::tuple). Функции foreach, map и call
    +3

    Раз уж такая пьянка:


    1. Не учитываются пустые тюплы, нужно, для unused, хотя бы 1н элемент
    2. Не учитывается перегруженный operator,() для случая, когда F возвращает user-defined тип, для которого и определён operator,()
    3. Раз уж C++ 14, тогда и decay_t<T> вместо typename decay<T>::type (tuple_size_v можно аналогично заюзать, но уже C++ 17)
    4. decay<T> — слишком жёстко, просто — remove_reference<T>

    template <typename F, typename T, size_t... i>
    void tupleForeachImpl(F&& f, T&& t, index_sequence<i...>) {
        auto unused = { true, (f(get<i>(forward<T>(t))), void(), true)... };
    }
    
    template <typename F, typename T>
    void tupleForeach(F&& f, T&& t) {
        tupleForeachImpl(forward<F>(f), forward<T>(t), make_index_sequence<tuple_size<remove_reference_t<T>>::value>());
    }

    Ну и, я думаю, стоило бы упомянуть C++ 17 fold expressions

  • Разделяемые указатели и многопоточность. И снова о них, в который раз
    0

    Да, нужно использовать atomic-операции для свапа: C++ 11 std::atomic_...<std::shared_ptr>

  • Какой язык программирования стоит выучить первым? (ʇdıɹɔsɐʌɐɾ: ɯǝʚɯо ņıqнqvиʚɐdu)
    0

    Достойно. Спасибо за ссылку. Получил массу удовольствия :)

  • Умный указатель для Pimpl
    0

    Спасибо за ответ.


    если забыли конструктор, ты будет первое сообщение, если забыли деструктор — второе.

    Я немного не понял вас. Вот моя логика: sizeof(T) — приведёт к ошибке компиляции, если компилятор не видет определения типа T, т.е., T — неполный тип. sizeof(T) никак не зависит от того определён для user defined типа конструктор или деструктор. Это означает, что sizeof(T) > 0 всегда — для нашого случая — нужен только для того, чтобы выдать пользователю ошибку во время компиляции о том, что он забыл определить указанный класс (тип) до места его использования (кстати, поскольку sizeof(T) никогда не может быть 0м — то можно просто писать sizeof(T)).
    Дальше: у вас, по сути, таких места 2: конструктор и деструктор. Ставим вопрос — может ли пользователь написать такое использование PimplPtr<Impl>, чтобы Impl был, например, определён до вызова конструктора PimplPtr<Impl> и, одновременно, не определён при вызове деструктора PimplPtr<Impl>? (или наоборот). Ответ — да, может:


    Случай первый: в конструкторе тип - неопределён, в деструкторе - уже определён
        template<typename T>
        struct PimplPtr
        {
            // 
        };
    
        // Header
        // 
        struct UserType
        {
            struct Impl;
            PimplPtr<Impl> _impl;
    
            UserType()
                // Используем неполный тип `Impl`
                : _impl{}
            {
            }
    
            ~UserType();
        };
    
        // Source
        // 
    
        struct UserType::Impl
        {
        };
    
        // Используем уже определённый тип `Impl`
        UserType::~UserType() = default;
    

    Случай второй: в конструкторе тип - определён, в деструкторе - не определён
        // Header
        // 
        struct UserType
        {
            struct Impl;
            PimplPtr<Impl> _impl;
    
            UserType();
    
            ~UserType()
            {
                // Используем неполный тип `Impl`
            }
        };
    
        // Source
        // 
    
        struct UserType::Impl
        {
        };
    
        // Используем уже определённый тип `Impl`
        UserType::UserType()
            : _impl{}
        {
        }
    

    Т.е., да — я вас обманул в деструкторе — для приведённого второго случая — такой assert будет полезен, прошу прощения. Но текст сообщения, всё же, немного неправильный.


    По поводу делегирующего конструктора — я снова вас обманул — имел в виду, что 1й конструктор должен вызывать 2й, т.е., для вашего кода, это что-то типа:


    PimplPtr(): PimplPtr(std::make_unique<T>()) {}
    
    explicit PimplPtr(std::unique_ptr<T>&& p) noexcept: p_(std::move(p)) {}

    И ещё — поскольку у вас в публичном классе есть функция:


    PimplPtr(std::unique_ptr<T>&& p)

    То это означает, что я могу сделать так:


    PimplPtr<int>{std::unique_ptr<int>{nullptr}};

    либо, более неявно — через какую-то фабрику:


    PimplPtr<T>{make_T()};

    где make_T() вернёт нулевой указатель.
    Т.е. не хватает проверки времени выполнения инварианта указатель не nullptr — я бы добавил assert() в конструктор и ф-и get().


    И, моё предвзятое мнение — в мире плюсов — для таких штук как умные указатели — пытаются убежать от неявных опеаторов преобразования: т.е. — пометить бы ещё operator ElementType() как explicit...


    И… и я бы написал using ElementType = T — меньше кода и проще читается…
    Извините, я увлёкся — простите за, возможно, нелепую критику — ухожу от клавиатуры :)
    Спасибо

  • Умный указатель для Pimpl
    +6

    Я бы всё-же не приводил "не очень хороший" код в статье для новичков: в первом примере, раз вы явно говорите об деструкторе, то можно бы и явно прописать реализацию конструкторов/операторов присваивания, следуя хорошему тону — The rule of three/five/zero. auto_ptr я бы выбросил вообще — всё по тому же поводу — deprecated — можно просто ссылочку дать — так для истории и действительно интересующихся. Про unique_ptr, возможно, нужно было бы рассказать, почему деструктор нужно реализовывать в файле-реализации (а что с move-семантикой на этот счёт ?) — но это отклонение от темы — зачем нужна своя реализация PimplPtr-а?


    Если убрать из статью вводную, то причина у вас, получается одна — "нарушение логической константности" (а точнее 2ве: "propagate_const пока не является частью стандарта"). Хорошо, но — код из статьи — не компилируется.


    По поводу реализации:


    • static_assert — не нужен — это сделает за вас вызов конструктора в make_unique.
    • constexpr — зачем? Он сдесь только мешает код читать и нет случая, когда он нужен был бы.
    • 2й конструктор, который explicit (кстати, а почему? почему и не первый ?), наверное, должен вызывать первый (Delegating constructor) — хотя бы для того, чтобы логика в конструкторах совпадала
    • зачем в деструкторе делать тот-же statis_assert, когда в конструкторе — она уже есть ?
    • почему здесь = default используется, а в примерах выше, в файле-реализации — нет ?: Widget::~Widget() = default
    • хорошим тоном является использование одной и той же функции — основной — для реализации других ф-й, которые дублируют код для удобства, т.е. — реализация операторов должна использовать эталонный get()

    Спасибо за статью. Извините за, возможно, резкий тон. Хорошего вам дня ^_^

  • Встраивание функциональных объектов, функций и лямбд через шаблоны и унификация при помощи virtual на C++
    0

    Спасибо.

  • Встраивание функциональных объектов, функций и лямбд через шаблоны и унификация при помощи virtual на C++
    0

    Не, долго писать, но вкратце:


    С использованием move(args)… компилятор попробует получить rvalue из всего переданного, и будет искать void (string &&, vector &&), опять промах. Остаётся использовать std::forward.

    Если вы будете использовать std::forward() в этом контексте, то это аналогично std::move() для всех параметров. Т.е., с вашим примером, будет std::move() как для 1го аргумента, так и для второго, т.е., если была бы ещё перегрузка moveNamedVector(string&&, vector&&), то вызвалась бы именно она, независимо от того использовали ли бы вы std::move() (более идиоматично) либо std::forward() (нестандартное использование).


    По поводу std::function, да, использование с аллокаторами задепрекейтили в C++17: Deprecating Allocator Support in std::function, потому что:


    there are technical issues with storing an allocator in a type-erased context and then recovering that allocator later for any allocations needed during copy assignment.

    Забавно

  • Встраивание функциональных объектов, функций и лямбд через шаблоны и унификация при помощи virtual на C++
    +1

    Допустим, у нас есть:


    #include <cstdio>
    #include <utility>
    
    struct UserType
    {
    };
    
    // (1)
    void Concrete(UserType&&)
    {
        std::puts("Concrete(UserType&&)");
    }
    
    // (2)
    void Concrete(UserType&)
    {
        std::puts("Concrete(UserType&)");
    }
    
    template<typename T>
    void DoForward(T&& parameter)
    {
        Concrete(std::forward<T>(parameter));
    }
    
    int main()
    {
        UserType value;
        // 1: аргумент (@value) у нас T& (UserType&, lvalue),
        // а параметр (@parameter) - T&& - происходит наложение
        // & + && -> получаем UserType&.
        // Вызывается (2)я версия Concrete()
        DoForward(value);
    
        // 2: Передаём временный обьект - 
        // аргумент (@value) у нас T&& (UserType&&, грубо говоря, rvalue),
        // а параметр (@parameter), всё тот же, T&& - происходит наложение
        // && + && -> получаем UserType&&.
        // Вызывается (1)я версия Concrete()
        DoForward(UserType{});
    }

    parameter в DoForward(), поскольку это шаблонная функция, попадает в (не знаю как перевести) "deduced context" — и всё что в main-е я написал, это, как раз таки, "вывод типа" параметра, который учитывает "шаблонный" тип параметра и тип аргумента функции. Из-за того, что это шаблон, как видно, мы можем получить либо rvalue либо lvalue. Всё просто: "deduced context" — юзаем forward().


    Как работает forward(): для первого вызова в main-е, как я уже написал, тип parameter вывелся в UserType&, т.е., T — это UserType&. forward() принимает аргументом (всегда!) — именованную переменную — т.е., это всегда lvalue. Получаем, что forward() имеет на вход шаблонный аргумент типа UserType& и тип параметра, так же, UserType& — всё это означает, что переданный на вход аргумент — это lvalue! forward() ничего не делает.
    Аналогично, для второго вызова в main-е, тип parameter вывелся в UserType&&, т.е., T — это UserType&&. Получаем, что forward() имеет на вход шаблонный аргумент типа UserType&& и параметр UserType& — переданный на вход аргумент — это rvalue! forward() делает move().


    Во всех остальных случаях — у нас нет "deduced context-а" и всё, каким мы его видим, таким и есть — т.е. T&& — это rvalue — нужно делать move(). T& — это ссылка — делаем что хотим, а T — хм, переменная, которая, больше не используется, поэтому можно сделать move().


    Т.е., в случае:


    template<typename T>
    struct NonDeducedContext
    {
    
        static void call(T parameter)
        {
            DoForward(std::move(parameter));
        }
    }

    для параметра функции call() нет вывода типа, потому что он уже выведен для класса в целом (мы его указываем при инстанциировании шаблона: NonDeducedContext<UserType>). Т.е., parameter — это value type — это копия аргумента функции call(). Ниже, по коду, он нигде не используется, поэтому я спокойно его муваю, тем самым говоря, что я его больше не использую и "делайте со мной что хотите".

  • Встраивание функциональных объектов, функций и лямбд через шаблоны и унификация при помощи virtual на C++
    +1

    Просто хочу заметить, что "если не сильно менять и причёсывать" код из статьи, то, как раз таки, std::forward() там нигде не нужно, просто потому что форвардинг ссылки нигде и не используются (а должны! К сожалению, и std::move() упущен… ладно).


    По поводу вашей реализации, извините, я не сильно всматривался, вам не хватило аллокатора который есть в интерфейсе std::function?
    Плюс, по поводу std::forward(), в этом месте — кхм, навскидку не понял, почему там std::forward(), а не std::move()? Судя по этому месту, я дико извиняюсь, — вы неправльно используете std::forward()?

  • Пример использования policy-based design в С++ вместо копипасты и создания ООП-шых иерархий
    0

    Хм, мне кажется, я начинаю завидовать вашему code-style.
    Да-да, если кодовая база однородна и всё такое, то, вроде как, всё хорошо, но как же меня бесит этот camelCase..

  • Преобразование обычного класса в странно повторяющийся шаблон
    0

    Как по мне — так тут один решающий недостаток — нельзя написать просто: std::vector<std::unique_ptr<IA>>

  • Внешняя сортировка с O(1) дополнительной памяти
    +2
    А зачем вы делаете new std::vector<T>? Это никак не влияет на скорость/пам'ять?
  • Инкапсуляция интерфейсов. Делаем API в C++ удобным и понятным
    0
    Погуглите pimpl unique_ptr: 1, 2, 3
  • Еще одно сравнение производительности С++ и C#
    0
    кхм, как вы умудряетесь так много и постоянно комитить )?
  • Еще одно сравнение производительности С++ и C#
    +1
    И вектор хранит свои элементы в куче.

    Это если по-умолчанию, передаём что-то типа вот такого аллокатора — и всё на стеке
  • Еще одно сравнение производительности С++ и C#
    +6
    к сожалению, речь о идиоматичности не идёт — пузырьковую сортировку, написанную ручками, кто использует?
  • Сравнение производительности С++ и C#
    +1
    Ну, как по мне, смысла нет. И так ясно: если стремиться написать код, который должен очень быстро работать, то качественное решение на C++ выиграет у качественного решения на C#. Другой вопрос в том, какие затраты на написания качественного решения: для C++ они выше. Как по мне, если брать среднестатистического программиста, то C# выигрывает в плане быстроты и удобства разработки, что вполне окупает потерю производительности.
  • Новый профилировщик памяти в Visual Studio 2015
    +3
    Я смотрел какое-то видео, увидел это окошко и именно из-за него захотел Visual Studio 2015! Ещё не пользовался, но — УРА, круто, наконец-то. Дождались!
  • Настоящее и будущее C++. Интервью с Эриком Ниблером
    0
    Ну зачем столько сарказма? Когда используешь cdb (другие не пробовал) из-под QtCreator-а, лично у меня — он подвисает очень часто и, по сравнению со встроенным Visual Studio Debugger-ом — всё продвигается медленнее. Просмотр переменных в QtCreator-е просто ад и ужас, особенно, если это обычные строки. Watch-окна, вроде как, нет (с удобными штуками такими как @ err,hr). Окна потоков/подключённых процессов так же как и окна пам'яти/call stack-а — удобнее в студии. Такой штуки как Autos в QtCreator-е, если я не ошибаюсь, вообще нет (так же как и дизасемблера ?). Коннектиться к удалённому процесу — намного проще, если ещё и к vmware-виртуалке (удобный плагин, всё в один клик). Как подключить KD — я так и не нашёл (хотя, признаюсь, гуглил не больше 2х минут).