Насколько я понимаю, в МММ и прочих финансовых пирамидах организатор обещает, что приобретение этой бумаги принесет некоторый доход (условно, заплати мне сейчас 100 рублей, а через год получишь от меня 200). Собственно, ради этого дохода их и приобретают. Валюта же — это просто условные фантики, относительно которых люди договорились, что будут принимать их в качестве оплаты своих услуг. И служит она исключительно для упрощения товарообмена между этими людьми.
Мне кажется, вас куда-то не в ту степь понесло. Детерминистично просто значит, что объект будет уничтожен сразу же, как только умрет последний указатель, а не в неопределенный момент времени после этого, когда будет запущен сборщик мусора.
Мне кажется, с переменными та же самая история, что и с параметром шаблона. Во-первых, компилятор должен проверить, что значение, которым она инициализируется, вычислимо в 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 ваша цитата неверна, об этом есть где-то ближе к концу моего предыдущего комментария. Суть в том, что есть три базовых категории и все попытки рассуждать о них, как о двух (как, например, в вашем тексте), на мой взгляд, некорректны и обречены на определенное словесное жонглирование. Мне больше по душе вот эта ссылка.
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 контейнеры хранят указатели на внутренние данные
К тому же, явно определённые конструкторы — формально ломают POD.
К слову, начиная с C++11, требования заметно ослабили: например, теперь можно и пользовательские конструкторы (главное, чтобы дефолтный конструктор, деструктор, а также конструкторы и операторы копирования и перемещения были тривиальными), члены данных не обязаны быть public (но все должны иметь одинаковую категорию видимости), разрешено наследование в ограниченном объеме (члены данных могут быть определены только в одном классе из иерархии).
Играй на победу-7: разбор примеров принятия решений с неполной информацией.
Имхо статья совсем не об этом (особенно неясно, при чем здесь «неполная информация»). Она, скорее, о наличии в каждой относительно сложной игре ряда простых стратегий, которые приводят к легкой победе в случае, если противник не знает о существовании такой стратегии и способах борьбы с ней, за счет того, что полностью ломают ему привычный шаблон игры.
Справедливости ради, есть ещё третий способ: специализировать шаблон mat для квадратных матриц и добавить в эту специализация характерные только для таких матриц методы.
либо что будет использован доступный компоновщику код в случае static-сборки.
Проблема в конкретной реализации оператора new: если std::cout вызовет его в процессе своей инициализации, то это приведет к записи в ещё неинициализированный поток. Но вопрос не в этом, а в том, почему clang выкинул вызов этих операторов из сгенерированного кода, он вроде как не мог во время генерации знать, сделаем мы такое переопределение или нет.
Это не работает, как способ сказать компилятору «по указателю лежит Derived», это говорит компилятору лишь «по указателю лежит Derived или производный от него тип». Это работает только если Derived объявлен как final, тогда компилятор понимает, что производных типов быть не может, и девиртуализирует вызов.
Соберите их вместе в один исполняемый файл и посмотрите на вывод. Не уверен, что этот код на 100% безопасен, т.к. он полагается на то, что std::cout не использует ::operator new() для своей инициализации (иначе произойдет запись в ещё неиницилизированный поток), но на gcc это работает. Разумеется, можно делать что-то другое (например, глобальную переменную-счетчик крутить) и тогда этой проблемы не будет.
Нет, с компилятором все в порядке, но агрессивность оптимизации отрицать бессмысленно.
А меня, честно говоря, этот момент смущает. Насколько я понимаю, ассемблерный вывод идет на уровне получения объектного файла, а не готового исходника, и в этот момент компилятор ещё не может знать, что у нас не будет других единиц трансляции. Суть в том, что в любой единице трансляции мы можем подменить глобальные operator new() и operator delete() на свою реализацию, и компилятор будет обязан использовать эту реализацию для всей программы. Поэтому то, что он выкинул здесь new и delete, а, соотвественно, и вызовы этих глобальных операторов, может изменить наблюдаемое поведение.
Но вообще ваше желание странновато.
constexpr
, например:Во-вторых, переменная может быть использовано в контексте, требующем знания её значения в момент компиляции (параметр шаблона, размерность массива,
static_assert()
), поэтому, возможно, компилятору тупо проще вычислить значение сразу, чем проводить анализ, а будет ли оно нам нужно, или же сохранять какие-то данные для ленивого вычисления.constexpr
в compile-time», но для gcc в документации я такой не нашел.Если память не изменяет, даже видел, как доллар использовали как имя макроса в какой-то C библиотеке.
Ну и справедливости ради, rvalue ссылка должна бы быть похожей на lvalue ссылку, а первые три функции в вашем списке пришли из C.
Во-вторых, это не объясняет, откуда это понятие вообще взялось и зачем оно нужно. Суть тут в следующем: в стандарте С++ при описании механизма вычисления выражений ссылочность не используется: «If an expression initially has the type "reference to
T
", the type is adjusted toT
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&&
), тем самым из результата такого выражения возможно перемещение.Кроме
std::array
public
(но все должны иметь одинаковую категорию видимости), разрешено наследование в ограниченном объеме (члены данных могут быть определены только в одном классе из иерархии).mat
для квадратных матриц и добавить в эту специализация характерные только для таких матриц методы.new
: еслиstd::cout
вызовет его в процессе своей инициализации, то это приведет к записи в ещё неинициализированный поток. Но вопрос не в этом, а в том, почему clang выкинул вызов этих операторов из сгенерированного кода, он вроде как не мог во время генерации знать, сделаем мы такое переопределение или нет.Derived
», это говорит компилятору лишь «по указателю лежитDerived
или производный от него тип». Это работает только еслиDerived
объявлен какfinal
, тогда компилятор понимает, что производных типов быть не может, и девиртуализирует вызов.final
, то нет, вдругbase_ptr
указывает не наconcrete_type
, а на производный от него тип c переопределенной в немfunction
.{
auto ptr = new int;
delete ptr;
}
std::cout
не использует::operator new()
для своей инициализации (иначе произойдет запись в ещё неиницилизированный поток), но на gcc это работает. Разумеется, можно делать что-то другое (например, глобальную переменную-счетчик крутить) и тогда этой проблемы не будет.operator new()
иoperator delete()
на свою реализацию, и компилятор будет обязан использовать эту реализацию для всей программы. Поэтому то, что он выкинул здесьnew
иdelete
, а, соотвественно, и вызовы этих глобальных операторов, может изменить наблюдаемое поведение.