Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>PCH, при сборке проектов с разными параметрами, нужны разные. При такой ситуации, когда PCH генерируется один раз при сборке всего проекта, если какой-то заголовок используется только один раз — то выигрыша не получается (для этого заголовка).
Почему же не получается? PCH же можно собрать один раз, а при последующих компиляциях использовать готовый — вполне себе выигрыш.
N>>//another.h
N>>// - форвардной декларации вложенных классов
N>>struct foo::doo;
N>>
J>Как насчет прав доступа? Вложенный класс ведь может быть объявлен приватным...
Гм.. а они не и нарушаются. Дружба она ж в обратную сторону действует.
Т.е. суть в том что бы doo из foo получил доступ к закрытым полям/методам some.
Но вот к сожалению синтаксис объявления такой дружбы в языке не предусмотрен.
Здравствуйте, rusted, Вы писали:
EP>>PCH, при сборке проектов с разными параметрами, нужны разные. При такой ситуации, когда PCH генерируется один раз при сборке всего проекта, если какой-то заголовок используется только один раз — то выигрыша не получается (для этого заголовка). R>Почему же не получается? PCH же можно собрать один раз, а при последующих компиляциях использовать готовый — вполне себе выигрыш.
Выделенное это исходные данные. При описанной ситуации — выигрыша не получается.
Если же, например, сборка всего лишь одна, один компилятором, параметры define-ы всегда одни и те же (ну или например всего два набора) — то да, тут PCH вполне решают.
Здравствуйте, Nikita.Trophimov, Вы писали:
A>>и еще конструктор/деструктор переименовать в this и ~this
NT>Зачем?
А почему нет, собственно? Какое преимущество у констуктора/деструктора, называющегося точно как класс?
Имя в любом случае прибито гвоздями: Если класс называется Т, то и конструктор обязан называться Т, а деструктор — ~T. Это ровно то же самое, что this, за исключением того, что ты можешь при переименовании класса тебе не нужно будет лезть и переименовывать конструкторы и деструктор.
Единственный довод за старое имя, который я вижу — это явный вызов деструктора через ptr->~T(), но и тут тоже ~this нормально сработал бы, разве что не так наглядно (с другой стороны, сейчас есть возможность совершить ошибку, вписав не тот деструктор, а, например, деструктор базового класса, который, если не виртуальный, не сделает всего, что нам нужно — с ~this это будет невозможно — деструктор будет определяться по типу ptr).
Ну и можно сделать эту фичу дополнением и разрешать и то, и другое, так что старый код от этого ломаться не будет, а новый только выиграет.
Еще довод: сейчас при использовании макросов всюду надо передавать имя класса, а так не надо будет, можно будет просто иметь макрос с телом класса, который сгенерит все, что надо, пользуясь class-agnostic-именами типа this.
J>Как насчет прав доступа? Вложенный класс ведь может быть объявлен приватным...
Вообще ИМХО дружба классов в С++ немного недоделана.
Как известно другом может быть что угодно. Мне не нравится сам факт что если class лежит в namespace, то объявление дружбы уже требует форвардной декларации.
namespace ns {
class foo {
class doo {};
};
}
class foo {};
//другой хидер
//fw
namespace ns {
class foo;
};
class some {
friend class foo;
friend class ns::foo;
//а вот с doo облом...
};
ИМХО раз уж после friend нужно писать class (или struct), то следовало бы оставить и слово namespase.
Объявление стало бы сложнее (но опять таки ИМХО никого ж не пугают километровый синтаксис с шаблонами), но и дружба стала бы более "широкой".
Т.е. типа такого:
// здесь никакого форвардного обявления класса из ns
class some {
friend class foo; //осталось как и было
friend namespace ns class foo; // это ns::foo
friend namespace ns class foo class doo; //это ns::foo::doo
};
Здравствуйте, jazzer, Вы писали:
J>А почему нет, собственно? Какое преимущество у констуктора/деструктора, называющегося точно как класс? J>Имя в любом случае прибито гвоздями: Если класс называется Т, то и конструктор обязан называться Т, а деструктор — ~T. Это ровно то же самое, что this, за исключением того, что ты можешь при переименовании класса тебе не нужно будет лезть и переименовывать конструкторы и деструктор.
Надо бы перечитать Строуструпа, я не помню, он как-то объясняет, зачем сделал именно так?
Видимо, по идейным соображениям, чтоб множественное наследование красиво выглядело:
class Foo : Bar, Buz
{
Foo() : Bar(123), Buz(456) {}
// versus:
ctor() : super(123), ????(456) {}
// хотя что мешало сделать
ctor() : Bar(123), Buz(456)
};
Либо это экономия на синтаксисе, — чтобы минимизировать количество новых ключевых слов. Отсюда и ~T вместо dtor.
J>Единственный довод за старое имя, который я вижу — это явный вызов деструктора через ptr->~T(), но и тут тоже ~this нормально сработал бы, разве что не так наглядно (с другой стороны, сейчас есть возможность совершить ошибку, вписав не тот деструктор, а, например, деструктор базового класса, который, если не виртуальный, не сделает всего, что нам нужно — с ~this это будет невозможно — деструктор будет определяться по типу ptr).
Деструктор можно вызвать функцией
template<class T> void destruct(T* t) { if(t) t->~T(); }
// делать версию с T& опасно,
// т.к. при явном указании параметров мы можем отхватить пользовательское приведение типа
// деструктор полного типа
destruct(&foo);
// деструктор базы
destruct<Bar>(&foo);
destruct((Buz*)&foo);
J>Ну и можно сделать эту фичу дополнением и разрешать и то, и другое, так что старый код от этого ломаться не будет, а новый только выиграет.
Вот только не надо this. Это дурацкая экономия на спичках, перегрузка смысла ключевого слова.
Чем микрософтовские стандарто-совместимые __ctor и __dtor не приглянулись?
J>Еще довод: сейчас при использовании макросов всюду надо передавать имя класса, а так не надо будет, можно будет просто иметь макрос с телом класса, который сгенерит все, что надо, пользуясь class-agnostic-именами типа this.
Пока что могу предложить такой способ генерирования агностического класса
template<class T> class agnostic;
#define BEGIN_CLASS(K) class K; \
template<> class agnostic<K> { \
typedef K target_type;
#define CTOR() agnostic() { ..... }
#define CCTOR() agnostic(const agnostic&) { ..... }
#define DTOR() ~agnostic() { ..... }
#define ASSIGN() agnostic& operator=(const agnostic&) { ..... }
................ ..............
#define END_CLASS(K) }; \
class K : public agnostic<K> {};
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Кстати, если swap реализован как 3 assignment'а (не важно — move или copy) — то получается три swap'а это 9 assignment (N-1)*3. EP>А если делать нормальный cycle/rotate по получается 5 assignment (N+1).
Думаю, если это будет встроенным оператором, то оптимизатор справится. В простых случаях это будет сделано вообще без присваиваний, просто регистры начнут соответствовать другим переменным.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Ops, Вы писали:
EP>>Кстати, если swap реализован как 3 assignment'а (не важно — move или copy) — то получается три swap'а это 9 assignment (N-1)*3. EP>>А если делать нормальный cycle/rotate по получается 5 assignment (N+1). Ops>Думаю, если это будет встроенным оператором, то оптимизатор справится.
1. Чтобы оптимизатор справился в общем случае, а не только для POD'ов — ему это нужно разрешить игнорировать side-effect's assignment дополнительными пунктами в стандарте, которые закрепят семантику assignment. Сейчас такое закрепление семантики есть для копий, которое разрешает copy elision.
2. Зачем делать плохой интерфейс, который не позволяет выразить всю мысль целиком, и соответственно требует усилий оптимизатора + дополнительных пунктов в ISO, чтобы быть эффективным? Почему сразу не сделать нормальное обобщение swap'а — cycle_left, как это сделал Александр Степанов? Тем более при наличии вариадиков. Да и выглядит оно красивее:
cycle(a,b,c,d,e);
vs
a:=:b:=:c:=:d:=:e;
Ops>В простых случаях это будет сделано вообще без присваиваний, просто регистры начнут соответствовать другим переменным.
swap крайне редко используется в случаях, где возможно просто "переосмысление" что чему соответствует.
Здравствуйте, Кодт, Вы писали:
J>>Ну и можно сделать эту фичу дополнением и разрешать и то, и другое, так что старый код от этого ломаться не будет, а новый только выиграет. К>Вот только не надо this. Это дурацкая экономия на спичках, перегрузка смысла ключевого слова.
Согласен, this перегружать не надо. Я думаю ноги у this растут из D:
class AbraCadAbRa
{
this()
{}
~this()
{}
}
К>Чем микрософтовские стандарто-совместимые __ctor и __dtor не приглянулись?
имхо, имя должно быть одно, что то типа self_type (но более лаконичное, + нужно постараться поломать как можно меньше кода новым keyword'ом), и желательно, чтобы его можно было использовать как имя типа, например при CRTP:
Здравствуйте, jazzer, Вы писали:
J>Здравствуйте, Nikita.Trophimov, Вы писали:
A>>>и еще конструктор/деструктор переименовать в this и ~this
NT>>Зачем?
J>А почему нет, собственно?
В C++ и так уже полно мест, где одно и тоже ключевое слово означает совсем разное: и static, и template, и typename. Когда ты уже 20 лет наблюдаешь развитие языка, то подобное ре-использование ключевых слов вроде и не добавляет сложности. Но вот многих, кто только начинает изучать язык, это всё сильно запутывает, и самое неприятное то, что до того как они во всём разберуться, они уже успевают понаписать достаточно кода, который потом приходится поддерживать.
Может и стоит сделать независящие от имени типа конструкторы и деструкторы, но уж точно не как this/~this. __ctor и __dtor для такого подойдут гораздо лучше.
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, jazzer, Вы писали:
J>>А почему нет, собственно? Какое преимущество у констуктора/деструктора, называющегося точно как класс? J>>Имя в любом случае прибито гвоздями: Если класс называется Т, то и конструктор обязан называться Т, а деструктор — ~T. Это ровно то же самое, что this, за исключением того, что ты можешь при переименовании класса тебе не нужно будет лезть и переименовывать конструкторы и деструктор.
К>Надо бы перечитать Строуструпа, я не помню, он как-то объясняет, зачем сделал именно так? К>Видимо, по идейным соображениям, чтоб множественное наследование красиво выглядело: К>
К>class Foo : Bar, Buz
К>{
К> Foo() : Bar(123), Buz(456) {}
К> // versus:
К> ctor() : super(123), ????(456) {}
К> // хотя что мешало сделать
К> ctor() : Bar(123), Buz(456)
К>};
К>
К>Либо это экономия на синтаксисе, — чтобы минимизировать количество новых ключевых слов. Отсюда и ~T вместо dtor.
Да не, вызов пусть будет таким же, это не критично. А вот само объявление конструктора — лень. В принципе, твоей третий вариант — о том же.
К>Вот только не надо this. Это дурацкая экономия на спичках, перегрузка смысла ключевого слова. К>Чем микрософтовские стандарто-совместимые __ctor и __dtor не приглянулись?
Да пофиг, в общем-то. Мне именно this не принципиален.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, Мишень-сан, Вы писали:
МС>>Лучше бы модули сделали нормальные.
EP>Daveed Vandervoorde: "Modules in C++"
Спасибо за видео, я пока только читал предложение Дэвида.
Поэтому и высказался, что лучше бы его добавили в TR.
Стрим мьютексы туда попали.
OpenMP туда попал.
А нормальная система модулей никому, похоже, не нужна. Ок, продолжаем жить при Header Hell.
Здравствуйте, Мишень-сан, Вы писали:
МС>Поэтому и высказался, что лучше бы его добавили в TR. МС>OpenMP туда попал.
Куда это OpenMP попал?
Я так понял это только proposal, и то там весь proposal это "а давай-те как-нибудь заюзаем OpenMP" без конкретики.
Надеюсь что никакое OpenMP в язык не пролезет.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Я так понял это только proposal, и то там весь proposal это "а давай-те как-нибудь заюзаем OpenMP" без конкретики. EP>Надеюсь что никакое OpenMP в язык не пролезет.
зря надеешься =)
OpenMP стандартизован и развивается. нет причин не включать его в стандарт С++.
но признаюсь, тот способ которым OpenMP реализуется, ниразу не соответствует идеологии С++. но, с другой стороны, если так мыслить, то и Cilk+ там не нужен.
в общем поглядим... но определенно, нужна какая-то поддержка многопоточности/синхронизации на уровне языка. иначе С++ получается самым сложным ЯП для разработки многопоточных программ.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>иначе С++ получается самым сложным ЯП для разработки многопоточных программ.
иначе С++ получается самым неподходящим ЯП для разработки многопоточных программ.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Здравствуйте, niXman, Вы писали:
X>зря надеешься =) X>OpenMP стандартизован и развивается. нет причин не включать его в стандарт С++.
То есть ты предлагаешь включать в ISO C++ любой убогий костыль, в корне не соответствующую генеральной линии языка, который хоть как-то стандартизирован?
X>но признаюсь, тот способ которым OpenMP реализуется, ниразу не соответствует идеологии С++.
В смысле синтаксис? ну да, я о том и говорю.
X>но, с другой стороны, если так мыслить, то и Cilk+ там не нужен. X>в общем поглядим... но определенно, нужна какая-то поддержка многопоточности/синхронизации на уровне языка. иначе С++ получается самым сложным ЯП для разработки многопоточных программ.
Поддержка нужна, и она прекрасно реализуется library-only. Microsoft PPL, Intel TBB — тому подтверждение.
Вот например:
1. В OpenMP Proposal они предлагают paralleltask, который по функциональности сильно пересекается с std::async. Но, почему-то их язык не устраивает — новый keyword подавай.
2. Предложенный ими "parallel for" — имеет свои семантические ограничения, ты не можешь любой for заменить на parallel for. Эти ограничения должны быть описаны в стандарте, захламляя core language.
В то время как library-only решение может забетонировать эти ограничения в API:
parallel_for_each( irange(0,100), [&](i) a[i]=i*2 );
or
parallel_transform( irange(0,100), a, [](i) i*2 );
Да, кстати, что делать например с parallel reduce, parallel transform/map? тоже вводить openmp'шные костыли?