Дядя Миша писал: Как вы знаете, можно инициализировать переменные, объявленные в глобальном пространстве и переменные, объявленные внутри функции, а вот переменные-члены класса нельзя проинициализировать прямо там же внутри описания класса.
Ты, пожалуйста, всегда добавляй - "в шестёрке". А то звучит странно для современного плюсовика.
Цитата:
Дядя Миша писал: Потому что никакой реальной инициализации в этом месте всё равно не происходит.
В каком "в этом" месте?
Цитата:
Дядя Миша писал: бедные программисты, которые очень бояться перетрудится и написать несколько лишних букв в массе своей стараются не писать это слово void
Они не пишут его потому, что оно избыточно, и зашумляет текст.
Ты небось тоже не пишешь для каждого объявления инта - signed int.
Цитата:
Дядя Миша писал: Нет, если серъезно, мне кажется что так красивее выглядит, поэтому и писал.
Ну это красота уровня public static final void.
Цитата:
Дядя Миша писал: Это вам не Раст, который анализирует исходник целиком.
Это как?
Цитата:
Дядя Миша писал: int foo( 5 ); // direct member initialization
XaeroX писал: Ты, пожалуйста, всегда добавляй - "в шестёрке". А то звучит странно для современного плюсовика.
До 11-й версии, если быть точным.
Цитата:
XaeroX писал: В каком "в этом" месте?
В месте объявления.
Цитата:
XaeroX писал: Они не пишут его потому, что оно избыточно, и зашумляет текст.
Глупазте. Четыре буквы текст зашумить не могут. Текст зашумляют бесконечные обращения к STL, вызов лямбд и прочая аналогичная пакость, которой болеет современный С++.
Цитата:
XaeroX писал: Это как?
А хрен его знает. Он же сперва парсит сорцы, а потом минут 20 думает о вечном.
Цитата:
XaeroX писал: А вот так у тебя можно?
А что такое foo в данном контексте?
Добавлено 07-08-2024 в 10:02:
Добавил также инициализацию через = поскольку это поддерживается при инициализации объектов на стеке и в куче, следовательно логично поддержать это и здесь.
C++ Source Code:
1
class CObject
2
{
3
vec3 origin( 10.0f, 20.0f, 30.0f ); // direct member initialization
4
vec3 angles( 30.0f, 20.0f, 10.0f ); // direct member initialization
5
vec3 scale = 1; // initialization by default
6
7
vec3 pool[10] = 0;
8
vec3 test[10]( 0.0f, 1.0f, 0.0f );
9
public:
10
int foo = 5; // direct member initialization
11
};
Но обратите внимание, конструкция вот такого вида
C++ Source Code:
vec3 scale = vec3( 1 );
не поддерживается, т.к. это не константное выражение. Члены класса можно инициализировать только константами.
В дальнейшем надо будет так же сделать инициализацию ссылок членов класса, поскольку они фактически были невозможными в старых версиях С++. Но я так и не смог придумать для чего подобное может понадобиться. Разве что представить ссылку в виде константного свойства только для чтения. Но в Шоте есть полноценные свойства. Так что пока не горит. Но для консистентности, разумеется надо будет потом сделать.
Дядя Миша писал: Текст зашумляют бесконечные обращения к STL, вызов лямбд и прочая аналогичная пакость, которой болеет современный С++.
Ну вообще идея STL-алгоритмов как раз повысить читабельность. Вместо цикла - понятное название, скажем std::find_if.
Цитата:
Дядя Миша писал: А что такое foo в данном контексте?
Ну скажем, глобальная функция. В плюсах можно написать вот так:
C++ Source Code:
1
int foo(int i) {
2
return sqrt(i);
3
}
4
5
struct C {
6
int i = foo(25);
7
};
И вот так можно написать - для эстетов:
C++ Source Code:
1
int foo(int i) {
2
return sqrt(i);
3
}
4
5
struct C {
6
int i = [](){ return foo(25); }();
7
};
А можно и вот так!
C++ Source Code:
1
int foo(int i) {
2
return sqrt(i);
3
}
4
5
struct C {
6
int i = 2*[](){ return foo(25); }();
7
};
Понятно, что вместо явно прописанных цифровых констант могут быть конст-переменные или макросы.
А могут быть другие функции. Вообще любые функции, они будут выполняться в момент создания экземпляра класса.
XaeroX писал: Ну а если остальным такое понадобится, они просто возьмут С++. Логично.
В шестёрке так допустим было нельзя, но все работали в ней и никто не жужжжал!
Впрочем, учитывая архитектуру Шота, я могу ещё много интересных конструкций добавить в язык, чисто в рамках компилятора. Интерпретатор останется прежним и совместимость тоже не пострадает.
Кстати. Порядок инициализации глобальных конструкторов в cinit определён порядком исходных файлов, подаваемых на вход компиляции.
А порядок исходных файлов в свою очередь нарушен быть не может, т.к. компилятор автоматически сортирует их по алфавиту. Таким образом, если вы делаете какой-то надкласс, который призван следить за всеми остальными, ну например отладчик памяти, файл с объявлением экземпляра должен называться таким образом, чтобы гарантировано стать первым в списке компиляции. Ну например с восклицательного знака.
Но в шестёрке есть баг, что если добавить файл и сразу же начать компиляцию, то он будет добавлен в список самым последним.
Помогает выгрузка - загрузка проекта. Но возможно что этот баг так до сих пор не исправили и в новых студиях.
В Шоте авто-сортировки по именам нету, можно вручную задавать приоритеты. Но да, принцип тот же.
Кстати этот момент технично обходится во всех учебниках по С++ и возможно вообще не подлежит стандартизации.
Добавил новый compile-time оператор is_polymorphic. Это аналог std::is_polymorphic, позволяет получить ответ, есть ли в классе виртуальные методы. Пригодится.
Интересное дело. НИГДЕ не удалось нагуглить даже малейших следов описания приоритета неявного приведения операторов преобразования типов. Эта тема, то ли считается неважной, то ли про нее тоже никто не знает. Приведу пример:
C++ Source Code:
1
class CVarFloat
2
{
3
float value;
4
public:
5
CVarFloat() : value(0.0f) {}
6
CVarFloat( float in ) : value(in) {}
7
operatorfloat() const { return value; }
8
};
Вот у нас есть такой симпатичный прокси-класс. И поскольку у него есть оператор приведения типа к float мы естественно хотим его умножить.
C++ Source Code:
1
CVarFloat a( 2.0f );
2
CVarFloat b( 2.0f );
3
4
float fc = a * b;
5
int ic = a * b;
6
bool bc = a * b;
7
Msg( "%g %d %d\n", fc, ic, bc );
Это нам выдаст 4 4 1. Почему в случае с bool получился 1 нас сейчас не особо интерисует, т.к. это всё равно невалидная операция. Явно не то, чтобы мы хотели получить. Интересно другое. Каким образом компилятор дотумкал во что ему надо преобразовывать типы? Но мы пойдем дальше.
Добавим явное преобразование в int. Вот так
C++ Source Code:
1
class CVarFloat
2
{
3
float value;
4
public:
5
CVarFloat() : value(0.0f) {}
6
CVarFloat( float in ) : value(in) {}
7
operatorfloat() const { return value; }
8
operatorint() const { return value; }
9
};
В результате получим три ошибки
C++ Source Code:
error C2593: 'operator *' is ambiguous
Но почему, собственно? Он теперь не может выбрать какой использовать? Значит на тип операнда в который пишется результат он не смотрит. Хорошо. Но и на тип второго операнда он смотреть не может. Это бы имело значение, если бы A был float или B был float. Однако у нас тут два класса, которые потенциально можно привести к какому-то простому типу.
Очевидно, раз компилятор НЕ МОЖЕТ полагаться на типы ABC, он выполняет приведение соответствуясь с некоторыми внутренними жестко установленными правилами. Ну а как ещё-то? То есть скажем в первую очередь пытается привести ко float, затем к int. Ну это навскидку. Я этих правил не знаю. И НАГУГЛИТЬ ИХ НИГДЕ НЕ УДАЛОСЬ АБСОЛЮТНО.
Никто про это даже не вспоминает. А возможно и не знает даже или просто не задумывается как работает эта магия.
Дядя Миша писал: Но почему, собственно? Он теперь не может выбрать какой использовать?
When two or more user-defined conversions that perform the same conversion are available at a conversion site, the conversion is said to be ambiguous. Such ambiguities are an error because the compiler can't determine which one of the available conversions it should choose
Моё мнение - весь этот сахар с операторами преобразования приводит к ошибкам, которые даже заметить порой сложно.
Лучше использовать каст методы с "говорящими" именами, toString(), toFloat() и т.д.
Ну я уже понял, что для выражения C = A x B где x арифметическое действие, нет никакой возможности выбрать правильный тип для преобразования. C может отсутствовать вообще, если выражение передается аргументом в функцию, финальный тип которого в свою очередь учитывает дедуктор перегрузки функций. Так что видимо да - только выбор из одного оператора. Сделал аналогично.
Добавлено сегодня в 00:01:
При этом разумеется кол-во операторов приведения типов по-прежнему может быть каким угодно, но в спорных случаях требуется явный каст.
Дядя Миша писал: Интересное дело. НИГДЕ не удалось нагуглить даже малейших следов описания приоритета неявного приведения операторов преобразования типов. Эта тема, то ли считается неважной, то ли про нее тоже никто не знает.
В данном случае в следующих двух выражениях выполняется одна и та же логика с implicit casts:
C++ Source Code:
a * b // calls operator*(float, float)
bar(a, b) // calls bar(float, float)
Соответственно, если добавить в класс operator int, то будет ambiguous в обоих случаях. Т.е. использование операторов ничем не отличается от вызова функций (во многих ситуациях).