Pull to refresh
1
0
Калмыков Юрий @Videoman

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

Send message

Потому-что на практике это не сложно сделать в виде сторонней библиотеки, а это принцип С++ - не тащить в компилятор то, что не нужно.
Есть подход big integer - когда число не ограничено по длине и фактически его придется хранить в динамической памяти (привет Питону). В этом случае придется расплачиваться быстродействием.
Есть подход long integer - когда необходимо работать с числами большими, чем поддерживаются процессором, но при этом фиксированной длины. Такие числа можно хранить на стеке и работать с ними почти также быстро как со встроенными, с поправкой на длину. Например, данный подход используется в Simple long integer math library for C++. Такие числа почти взаимозаменяемые со стандартными, не считая некоторых нюансов в автоматических преобразованиях.

Древнее оборудование? Метод близнецов - это же, по моему, buddy-allocator, который используется во всех операционных системах для выделения памяти. За счет скорости, простоты и неподверженности фрагментации, он идеально подходил для ядра ОС, где все страницы по замеру равны степени двойки. Он используется в ядре Linux, Windows, RTOS. Да и во многих современных Си-шных быстрых аллокатарах он используется как нижележащий слой, под аренами и прочими структурами. Так что, списывать его со счетов я бы не стал.

Ладно порядок, даже размер слова и байта пропустили.

Странно, что ничего не сказано про выравнивание. х86, по умолчанию, позволяет доступ к не выровненным данным, тогда как многие другие платформы жестко требуют выравнивания.

Всё верно. Это обратное следствие. Как всегда дилемма: скорость-память.

Также ещё из минусов make_shared: custom deleter не задать, aliasing constructor не вызвать.

Дополнения к std::shared_ptr и std::make_shared:
1. Без объяснения как на практике устроен std::shared_ptr, невозможно понять что за сценой присутствует control block и без std::make_shared у нас появиться дополнительный уровень косвенности и следственно лишнее выделение памяти под него. std::make_shared объединяет эти два выделения, делая создание и удалении быстрее.
2. std::weak_ptr естественно делает увеличение счетчика ссылок, но это отдельный счетчик, который также находится в control block-е.

На БК0010(01)/11/11M - на базе PDP-11 - не было целочисленных mul и div. op-коды были, но аппаратно команды были не реализованы. Даже сдвиг битовый был только одинарным: asl r0 - сдвинуть на один бит влево и вся роскошь. Чего уж тут говорить о Floating Point.

Под независимыми, я имел в виду «независимые» по коду, в конкретном модуле. Но их может связать сторонний по отношению к модулю класс, динамически. Многие просто об это забывают и из-за этого представляют себе возможности RTTI уж слишком примитивно.
Ну и что с того, что с наследует a и b? От этого связывание не становится статическим, а всё-равно происходит динамически в рантайме, при работе программы. Пофантазируйте: определения с у вас вообще может не быть, если подлинкована либа или подгружена dll с неизвестным вам классом.
(вопросы совместимости RTTI и целесообразности такого подхода на практике уберем за скобки)
Ну например, можно рассмотреть простейшую реализацию COM на dynamic_cast<>:
#include <iostream>
#include <memory>
#include <string>

// a.h include

struct a_base {

    virtual std::string who() = 0;
    virtual ~a_base() = default;
};

struct a : a_base {

    std::string who() override { return "a class"; }
};

// b.h include

struct b_base {

    virtual std::string who() = 0;
    virtual ~b_base() = default;
};

struct b : b_base {

    std::string who() override { return "b class"; }
};

// c.h include

struct c : a, b 
{
};

// somewhere else

b_base* create_b()
{
    return new c;
}

// main

int main()
{
    b_base* b = create_b();
    std::cout << b->who() << "\n";

    a_base* a = dynamic_cast<a_base*>(b);

    if (a != nullptr) {

        std::cout << "receive a_base from pointer to b_base\n";
        std::cout << a->who() << "\n";

    } else {

        std::cout << "can't receive a_base from pointer to b_base\n";
    }

    delete b;
    b = nullptr;

    return 0;
}
Обратите внимание, что иерархия класса a и класса b могут друг о друге ничего не знать, а также оба вообще могут не знать, что в программе есть класс, который наследует их оба. Однако это не мешает dynamic_cast отслеживать эту связь в рантайме и получать через b_base указатель на a_base.
В С++ всё это называет cross cast. Идея думаю понятна.
Динамик каст делает больше чем просто енамчик, там сохраняется информация о иерархии. Например с вашей реализацией вы не сможете привести к другой базе, то есть это не точно тот тип, но находящийся в той иерархии
На самом деле мало кто задумывается как работает dynamic_cast. С его помощью можно привести не просто к другому типу, который является наследником, а к типу вообще находящемуся во вне иерархии того или иного класса.
Всё верно. Как и многие конструкции в С++, if constexpr также не является «серебряной» пулей и не предназначена для замены механизма перегрузки функций. Следовательно её нужно использовать с головой, особенно при проектировании библиотек, которые будут использоваться сторонними программистами.
Действительно, если написать общий шаблон c диспетчеризацией, условно, что-то типа:
template <typename type_t>
type_t func(type_t arg)
{
    if constexpr (std::is_same_v<type_t, type1> {
        ...
    } else if constexpr (std::is_same_v<type_t, type2> {
        ...
    } else ...
}
такой подход, создаст массу проблем с расширением, особенно если он используется во внешнем API.
С другой стороны, для того, что бы заменить кучу бойлерплейта, построенного на частичных перегрузках классов внутри реализации, это самое то:
template<uint_t sidx, typename variant_t>
inline void print_variant(const variant_t& data, size_t idx)
{
    if constexpr (sidx < std::variant_size_v<variant_t>) {

        if (idx == 0)
            format(std::get<sidx>(data));
        else
            print_variant<sidx + 1>(data, idx - 1));
    } else
        throw v2::Exception("Variant index is out of range.");
}
— выводим значение std::variant по известному индексу в рантайме.
Полностью согласен, но хотел бы еще более конкретизировать:
1. Если требуется несколько сложное состояние, инвариант, в этом случае требуется объект, который и будет поддерживать это состояние через внешний интерфейс, в строгой согласованности.
2. Также объект может быть полезен, если некие данные абсолютно независимы друг от друга, могут меняться не согласованно, но по логике очень часто передаются вместе. Частный случай — структура.

Во всех остальных случаях функции предпочтительнее и гибче, на мой взгляд.
Да, только байт в адресе IPv6 — 16 ;)
Более того, начиная с С++20 битовое представление signed integers фиксировано стандартом как дополнение до двух (two's complement).
However, all C++ compilers use two's complement representation, and as of C++20, it is the only representation allowed by the standard, with the guaranteed range from -2^(N-1) to +2^(N-1) — 1 (e.g. -128 to 127 for a signed 8-bit type).

8-bit ones' complement and sign-and-magnitude representations for char have been disallowed since C++11 (via CWG 1759), because a UTF-8 code unit of value 0x80 used in a UTF-8 string literal must be storable in a char element object.
2 дня назад — первый релиз? поздравляю)
:) ну да, действительно, выглядит как-будто либу в ответ на комментарий настрочил. На самом деле она в продакшене используется уже пару лет, а вот протестить под несколько платформ, оформить и т.д. — руки могут год не доходить.
Пойду пару советов в issue закину
Спасибо, осмыслю тогда и отвечу, что бы не плодить офтопик тут.
Кстати, полностью поддерживаю сарказм. Вроде как сначала имели конвертеры в стандарте, потом в С++17 их вдруг задепрекейтили, без объяснений. Ничего нового не завезли. С u8"" строками вообще не понятно. Допустим до С++20 в качестве контейнера для UTF-8 я могу использовать std::string и u8"" строки, которые сейчас преобразуются к char. Интересно, как предполагается работать с UTF-8 в дальнейшем, когда у нас появляется тип char8_t, что с обратной совместимостью к тоннам уже существующих API?
Пока приходится пользоваться своим велосипедом: sutfcpplib, но почему-то ничего готового не появляется в стандарте, хотя бы базовые вещи :(?
По своей практике, могу сказать что отказ от буферизации на скорость записи не влияет, либо влияет в меньшую сторону. Да и заменить одно на другое не так просто. Это все-равно что поменять I/O API. Не буферизированный вывод, это работа со строго выровненными блоками данных, где всю работу кеша мы берем фактически на себя. Это всё скорее про случаи где важна надежность, чем скорость.
А мета-информация, например запись о длине файла и т.д.? Не знаю как под Linux, но под Windows также возможна запись без буферизации однако, что бы обновить размер файла, всё равно придется вызывать FlushFileBuffers (fsync).

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity