Search
Write a publication
Pull to refresh
23
0

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

Send message
Насколько я понимаю, в МММ и прочих финансовых пирамидах организатор обещает, что приобретение этой бумаги принесет некоторый доход (условно, заплати мне сейчас 100 рублей, а через год получишь от меня 200). Собственно, ради этого дохода их и приобретают. Валюта же — это просто условные фантики, относительно которых люди договорились, что будут принимать их в качестве оплаты своих услуг. И служит она исключительно для упрощения товарообмена между этими людьми.
Если уж вам так принципиален дефолтный деструктор, можно сделать так:
// Header file
class C {
    private:
        class Pimpl;
        struct Pimpl_deleter {
            void operator() (Pimpl* pimpl_ptr);
        };
        std::unique_ptr<Pimpl, Pimpl_deleter> pimpl_ptr;
};

// Source file
class C::Pimpl { ... };

void C::Pimpl_deleter::operator() (C::Pimpl* pimpl_ptr)
    { delete pimpl_ptr; }
Но вообще ваше желание странновато.
Мне кажется, вас куда-то не в ту степь понесло. Детерминистично просто значит, что объект будет уничтожен сразу же, как только умрет последний указатель, а не в неопределенный момент времени после этого, когда будет запущен сборщик мусора.
Мне кажется, с переменными та же самая история, что и с параметром шаблона. Во-первых, компилятор должен проверить, что значение, которым она инициализируется, вычислимо в compile-time, и если нет, то выдать ошибку компиляции. Причем для этой проверки недостаточно просто проверить, что функция и её аргумент constexpr, например:
int non_constexpr() { return 1; }

constexpr int f(bool b)
{
    return b ? 0 : non_constexpr();
}

constexpr int x = f(true); // OK
constexpr int y = f(false); // Compilation error

Во-вторых, переменная может быть использовано в контексте, требующем знания её значения в момент компиляции (параметр шаблона, размерность массива, static_assert()), поэтому, возможно, компилятору тупо проще вычислить значение сразу, чем проводить анализ, а будет ли оно нам нужно, или же сохранять какие-то данные для ленивого вычисления.
В любом режиме оптимизации считает в compile-time, как и положено по стандарту.
Честно говоря, я не уверен, что стандарт где-то обязывает так делать. В конце концов, у нас есть правило «as-if», а поскольку программы с вычислением выражения в compile-time и в runtime имеют одно и то же наблюдаемое поведение, то они обе корректны. На практике, конечно, понятно, что когда мы используем выражение как параметр шаблона, то компилятор его посчитает во время компиляции, просто чтобы проверить, что не будет ошибки компиляции, дать имя этому шаблону и т.д. Тут было лучше какую-нибудь опцию компилятора иметь, которая говорила бы ему «считай все constexpr в compile-time», но для gcc в документации я такой не нашел.
ну а двойной амперсант по аналогии с указателем на указатель
Ну это не совсем корректная аналогия, это же не ссылка на ссылку. И потом, три звездочки можно, а три амперсанда уже нет.
В терминах C++11 ваша цитата неверна, об этом есть где-то ближе к концу моего предыдущего комментария. Суть в том, что есть три базовых категории и все попытки рассуждать о них, как о двух (как, например, в вашем тексте), на мой взгляд, некорректны и обречены на определенное словесное жонглирование. Мне больше по душе вот эта ссылка.
Доллар же вроде можно как идентификатор использовать, по крайней мере такое в gcc компилируется:
int main()
{
    int $ = 5;
}
Если память не изменяет, даже видел, как доллар использовали как имя макроса в какой-то C библиотеке.

Ну и справедливости ради, rvalue ссылка должна бы быть похожей на lvalue ссылку, а первые три функции в вашем списке пришли из C.
lvalue определить сравнительно просто: это все что может стоять слева от знака присвоения '='.
Определение так себе, во-первых, это верно лишь для встроенного оператор присваивания, но не для перегруженного:
int f();
f() = 0; // Ошибка, в rvalue нельзя присвоить

struct T {};
T g();
g() = T{}; // Нет ошибки, хотя g() - rvalue

Во-вторых, это не объясняет, откуда это понятие вообще взялось и зачем оно нужно. Суть тут в следующем: в стандарте С++ при описании механизма вычисления выражений ссылочность не используется: «If an expression initially has the type "reference to T", the type is adjusted to T prior to any further analysis», то есть тип выражения всегда нессылочный, ссылки просто откидываются. А раз так, то нужен какой-то другой способ различать, является ли результат выражения адресом объекта указанного типа или же самим этим объектом (значением). Вот этой роли в стандарте и служат понятия lvalue/rvalue. Если представить себе, что все встроенные операторы — это тоже вызовы каких-то функций, то если соответствующая функция возвращала бы T& — это выражение lvalue типа T, возвращала бы T&& — xvalue типа T, возвращала бы просто T — prvalue типа T. Объяснение, конечно, немного с конца к началу, т.к. в обычном C никаких ссылок не было, а понятия lvalue/rvalue были (и служили той же цели), и ссылки в C++ были введены, как способ вернуть lvalue из функции, но для C++ программиста так должно быть понятнее имхо.

Также можно заметить, что в моем перечислении есть какое-то prvalue, а просто rvalue нету. Действительно, в C++11 базовых категорий три — это lvalue, xvalue и prvalue, и выражение может быть ровно одним из этого списка (до C++11 rvalue ссылок не было, поэтому категория xvalue отсутствовала, а prvalue называлась просто rvalue). Вместе с тем есть ещё два обобщенных понятия: glvalue = lvalue + xvalue и rvalue = xvalue + prvalue. Соотвественно, разделение glvalue/prvalue в С++11 выражает то самое классическое разделение «адрес объекта/значение», а вот само понятие rvalue выражает другую идею — идею перемещения. Основное свойство rvalue — им в отличии от lvalue может быть инициализирована rvalue ссылка (T&&), тем самым из результата такого выражения возможно перемещение.
На самом деле все stl контейнеры хранят указатели на внутренние данные
Кроме std::array
Попробуйте тут почитать, возможно понравиться больше, чем стандарт, хотя описание все равно формальное.
К тому же, явно определённые конструкторы — формально ломают POD.
К слову, начиная с C++11, требования заметно ослабили: например, теперь можно и пользовательские конструкторы (главное, чтобы дефолтный конструктор, деструктор, а также конструкторы и операторы копирования и перемещения были тривиальными), члены данных не обязаны быть public (но все должны иметь одинаковую категорию видимости), разрешено наследование в ограниченном объеме (члены данных могут быть определены только в одном классе из иерархии).
Играй на победу-7: разбор примеров принятия решений с неполной информацией.
Имхо статья совсем не об этом (особенно неясно, при чем здесь «неполная информация»). Она, скорее, о наличии в каждой относительно сложной игре ряда простых стратегий, которые приводят к легкой победе в случае, если противник не знает о существовании такой стратегии и способах борьбы с ней, за счет того, что полностью ломают ему привычный шаблон игры.
Ещё можно два конструктора объединить в один, впрочем это дело вкуса:
explicit IdOf(repr_type value = repr_type()) : value_(value) {}
Можно написать базовый класс с общими операциям и унаследовать оба варианта от него.
Есть два способа это сделать:
Справедливости ради, есть ещё третий способ: специализировать шаблон mat для квадратных матриц и добавить в эту специализация характерные только для таких матриц методы.
либо что будет использован доступный компоновщику код в случае static-сборки.
Проблема в конкретной реализации оператора new: если std::cout вызовет его в процессе своей инициализации, то это приведет к записи в ещё неинициализированный поток. Но вопрос не в этом, а в том, почему clang выкинул вызов этих операторов из сгенерированного кода, он вроде как не мог во время генерации знать, сделаем мы такое переопределение или нет.
Это не работает, как способ сказать компилятору «по указателю лежит Derived», это говорит компилятору лишь «по указателю лежит Derived или производный от него тип». Это работает только если Derived объявлен как final, тогда компилятор понимает, что производных типов быть не может, и девиртуализирует вызов.
Если функция или класс не final, то нет, вдруг base_ptr указывает не на concrete_type, а на производный от него тип c переопределенной в нем function.
Либо я плохо объяснил, либо вы неправы. Создайте такие два файла:
operators_replacement.cpp
#include <iostream>
#include <cstdlib>

void* operator new(std::size_t count)
{
	std::cout << "Allocation\n";
	return malloc(count);
}

void operator delete(void* ptr)
{
	std::cout << "Deallocation\n";
	free(ptr);
}

main.cpp
int main()
{
auto ptr = new int;
delete ptr;
}
Соберите их вместе в один исполняемый файл и посмотрите на вывод. Не уверен, что этот код на 100% безопасен, т.к. он полагается на то, что std::cout не использует ::operator new() для своей инициализации (иначе произойдет запись в ещё неиницилизированный поток), но на gcc это работает. Разумеется, можно делать что-то другое (например, глобальную переменную-счетчик крутить) и тогда этой проблемы не будет.
Нет, с компилятором все в порядке, но агрессивность оптимизации отрицать бессмысленно.
А меня, честно говоря, этот момент смущает. Насколько я понимаю, ассемблерный вывод идет на уровне получения объектного файла, а не готового исходника, и в этот момент компилятор ещё не может знать, что у нас не будет других единиц трансляции. Суть в том, что в любой единице трансляции мы можем подменить глобальные operator new() и operator delete() на свою реализацию, и компилятор будет обязан использовать эту реализацию для всей программы. Поэтому то, что он выкинул здесь new и delete, а, соотвественно, и вызовы этих глобальных операторов, может изменить наблюдаемое поведение.

Information

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