Re[2]: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 21.12.16 18:36
Оценка: 9 (1) +2
Здравствуйте, antropolog, Вы писали:

A>
A>const base& obj = true ? (const base&)d1() : (const base&)d2();
A>


Здесь будет dangling reference.
Re[7]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 22.12.16 12:30
Оценка: 12 (2)
night beast:

NB>блин. как-так.

NB>как D1Wrapper() может пережить конец выражения если напрямую нигде не используется?

Раньше было так: когда инициализируешь такую ссылку частью объекта (представленного prvalue выражением), то время жизни всего объекта целиком подлежит продлению:

The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: [...]

В новой редакции это правило оставили, но, перечитав 5.2.5, я-таки осознал, что не понимаю, как данное правило можно применять теперь, если prvalue.x — это xvalue, а не prvalue, как было раньше (как компилятор узнает, что некое xvalue — это чей-то subobject?). Как минимум понятно, что guaranteed copy elision при получении результата тернарного оператора в том примере всё же быть не может, т.е. насчёт корректности примера я был не прав (похоже, без перечитывания всего стандарта выпендриваться с новыми фичами особо нельзя, стандартизаторы усё попоменяли, так не и знаешь, откуда ждать подвоха ).
Re[7]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 23.12.16 15:42
Оценка: 7 (2)
tdiff:

T>Например, почему "получившееся xvalue уже не является temporary"?


Потому что не указано, что оно temporary Вот, смотри:

__declspec(dllimport) X *dll_func(X &&, X *);

int main()
{
    auto &&x = static_cast<X &&>(*dll_func(X(), new X));
    ....
}

Теоретически dll_func может вернуть указатель на временный объект, созданный выражением X(), но она также может вернуть и что-нибудь иное — например, указатель на объект, созданный выражением new X. Как компилятор узнает, ссылается ли выражение static_cast<X &&>(*dll_func(X(), new X)) на временный объект? Очевидно, что для продления времени жизни временного объекта как минимум требуется какой-то признак того, что объект действительно является временным. Если объект берётся из какого-то выражения, то таким признаком служит принадлежность выражения к определённому виду. Если выражение к соответствующему виду не принадлежит, то время жизни объекта, на который оно ссылается, продляться не будет (независимо от того, является объект временным или нет).
Re: ternary для разных подклассов на стеке
От: antropolog  
Дата: 21.12.16 18:21
Оценка: -1 :)
Здравствуйте, tdiff, Вы писали:

const base& obj = true ? (const base&)d1() : (const base&)d2();
Re: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 21.12.16 18:30
Оценка: +2
Здравствуйте, tdiff, Вы писали:

T>Другими словами, я хочу, чтобы стек выглядел вот так:

T>[ссылка на объект ниже]
T>[d1 или d2]
T>и время жизни d1 или d2 совпадало с текущим скоупом.
T>...
T>а создавать без повода объекты на хипе тоже как-то не круто.

Boost.Variant
Re[2]: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 23.12.16 18:20
Оценка: +1 :)
Здравствуйте, Erop, Вы писали:

T>>хочу, чтобы стек выглядел вот так:

T>>[ссылка на объект ниже]
T>>[d1 или d2]
T>>и время жизни d1 или d2 совпадало с текущим скоупом.
E>Что мешает написать то, что ты хочешь, прямо?

Видимо желание узнать о готовом решении а-ля boost/std::variant превалирует над желанием лисапедить
Re[4]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 23.12.16 20:38
Оценка: :))
Здравствуйте, Erop, Вы писали:

E>А динамический полиморфизм для чего?


Ради элитности же
Re[9]: ternary для разных подклассов на стеке
От: uzhas Ниоткуда  
Дата: 22.12.16 13:04
Оценка: 3 (1)
Здравствуйте, night beast, Вы писали:

NB>спасиб. наверно, пока такие трюки опасно применять...


нашел эпичный срач на эту тему: http://stackoverflow.com/questions/35947296/about-binding-a-const-reference-to-a-sub-object-of-a-temporary
Re[5]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 23.12.16 08:28
Оценка: 2 (1)
tdiff:

NI>>Разница в том, что во втором случае к моменту вызова f ссылка x указывает на мёртвый объект.


T>Почему, если base&& должна продлевать время жизни temporary?


Всё дело в мутном понятии "temporary". Под ним подразумеваются сразу и временный объект и выражение, которое обозначает этот объект. Выражению, которое мы используем в качестве инициализатора ссылки на временный объект, недостаточно просто ссылаться на данный объект, чтоб его время жизни продлялось, — само выражение должно быть "temporary" (если б продление времени жизни временного объекта работало для любого выражения-инициализатора, то как минимум возникли бы очевидные сложности с проверкой, является ли объект временным).

Выражение является temporary только если в правилах для него явно указано, что оно temporary, либо если оно непосредственно создаёт и возвращает некий temporary object, либо если оно эквивалентно чему-то, что является temporary (если temporary обернуть в круглые скобки, то получившееся выражение тоже будет temporary). Разумеется, нормальной формализацией тут и не пахнет, и одно лишь чтение стандарта не приводит к пониманию что есть что; нужно просто знать, что стандартизаторы имели в виду.

T>У меня есть догадка, что происходит что-то вроде:

T>1. создаётся temporary d1() с типом d1&&

Выражения никогда не имеют ссылочный тип. Типом выражения d1() будет d1.

T>2. тип этого объекта конвертируется в base&&


Формально там prvalue типа d1 преобразуется к xvalue типа base. Исходное prvalue является temporary, а получившееся xvalue уже не является temporary (хотя оно тоже ссылается на временный объект).

T>3. результатом ternary является base&&, которая продлевает время жизни d1


Нет там никаких ссылок.

T>этой ссылкой инициализируется ссылка base&& x, которая почему-то уже не продлевает жизни d1


x инициализируется non-temporary выражением, поэтому продление не применяется.
Re[9]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 26.12.16 11:44
Оценка: 2 (1)
tdiff:

T>А можешь подсказать, где указано, какие выражения temporary, а какие нет?


В стандарте явного указания нету (формальное описание того, что подразумевается, должно выглядеть примерно так, как описано в http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3918.html). Нечёткий признак даётся в описании value categories:

— An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment
expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated
with an object.

— A prvalue (“pure” rvalue) is an rvalue that is not an xvalue.

prvalue, которое ассоциировано с неким объектом, — это и есть пресловутое "temporary".

Кстати, инициализация ссылки иногда сама может создавать temporary object. Например, тут

int &&x = 1;

литерал 1 — это prvalue, не ассоциирование с каким-либо объектом. В процессе инициализации ссылки будет создан временный объект, инициализированный значением 1, и ссылка будет связана с данным объектом.
Re[5]: ternary для разных подклассов на стеке
От: antropolog  
Дата: 21.12.16 19:43
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>const base &x = d1();

ну собственно меня и заинтересовало чем принципиально тернарный оператор здесь отличается, потому как например в случае с const base& x = true ? d1() : d1(); провисшей ссылки не будет.
Re[6]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 11:36
Оценка: +1
Здравствуйте, N. I., Вы писали:

NB>>м... я так понимаю, очевидное предположение что bref_1.ref.f() -- это вызов функции удаленного объекта не соответствует действительности?


NI>Я там несколько слукавил насчёт "стандарта" — C++17 всё-таки ещё не вышел По C++14 тернарный оператор может создать копию выбранного результата, и тогда да, bref_1.ref.f() может быть вызовом функции для удалённого объекта.


блин. как-так.
как D1Wrapper() может пережить конец выражения если напрямую нигде не используется?
Re[5]: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 23.12.16 14:52
Оценка: +1
Здравствуйте, tdiff, Вы писали:

EP>>Во втором же случае, потребовалась бы продление времени жизни временной переменной до конца scope

T>Спасибо за подробный ответ, но по-моему у нас есть гарантия, что const & / && — ссылки продлевают жизнь временным объектам.

Не во всех случаях.
Re[4]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 23.12.16 20:05
Оценка: :)
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>>Видимо желание узнать о готовом решении а-ля boost/std::variant превалирует над желанием лисапедить


E>Я так понимаю, что всё это под флагом борьбы с оверхедом же?

E>Конечно подозрительная такая бескомпромиссная борьба, но фиг его знает-то, может людям реально надо...

Да нет, без флага. Из любопытства
Re[11]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 25.12.16 13:20
Оценка: -1
Здравствуйте, N. I., Вы писали:

NI>Теперь давай разберём выражение X — Y. Если один из операндов имеет тип std::size_t, а другой имеет некий знаковый целый тип T, то общим типом (который станет типом результата разности) будет либо std::size_t, либо T, либо promoted вариант одного из этих типов (promotion делается до int или unsigned int).


Разность знакового и беззнакового всегда знаковая...

NI>Уже нагородили раньше.

Ну так это же суперкритичное к ресурсам место, вроде?
Раз пул приковыряли, placeholder надо отковырять...

NI>Если код корректно работает, зачем его трогать?

Для поддерживаемости, например. Ну и для эффективности.

NI>Это из разряда "а почему бы с первого раза не сделать всё идеально?". Ну, вот не всегда так получается.

Это у криворуктих программистов не получается. А если писать так, что бы правильно делать было ПРОЩЕ, чем в стиле "вроде работает, так что не будем менять", то всё внезапно получается.

Смотри pimpl туда же в каких-то целях привернули, наверное? В тех же целях надо и ph Отковырять... просто надо до конца работу делать, а не халтурить.

NI>Если бы код продолжал корректно работать, то время можно было бы потратить на что-то другое.

В таком сценарии велосипед надо ВЫКИНУТЬ.

NI>Появление функции внутри функции — это типа не нагромождение? А если таких внутренних функций ещё и не одна?

Ну это типа использование прямо по назначению.
Не нравится лямбда, можно рядом с функцией шаблон написать, только с захватом контекста будут сложности.

NI>Попробуй сделать break или continue изнутри этой лямбды. Или, например, вернуть неперемещаемый объект, не имея доступа к компилятору с поддержкой guaranteed copy elision из C++17.


Про break/continue не совсем понятно. Типа речь идёт о том, что вся эта конструкция часть цикла?
Можно ветвиться по возвращаемому значению, например.

Что касается возвращения неперемещаемого объекта, то его можно захватить из контекста и заполнить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: ternary для разных подклассов на стеке
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 24.03.18 16:29
Оценка: +1
Здравствуйте, tdiff, Вы писали:

T>Хочется в зависимости от условия создавать на стеке объекты разных подтипов одного общего класса, но пока не получается:



А если выделить нужное количество памяти на стеке при помощи alloca, а натравить на эту память оператор new? я не помню, как такая форма называется, но вроде как-то можно. Ну, а потом уже взять указатель/ссылку на этот объект и присвоить куда надо
Маньяк Робокряк колесит по городу
ternary для разных подклассов на стеке
От: tdiff  
Дата: 21.12.16 18:11
Оценка:
Привет

Хочется в зависимости от условия создавать на стеке объекты разных подтипов одного общего класса, но пока не получается:
class base { virtual void f() const = 0;};
class d1 : public base { void f() const override {...} };
class d2 : public base { void f() const override {...} };

const base& obj = true ? (const base&)d1() : d2();
obj.f();

Ошибка:
<source>:7:25: error: allocating an object of abstract class type 'const base'
const base& obj = true ? (const base&)d1() : d2();

Другими словами, я хочу, чтобы стек выглядел вот так:
[ссылка на объект ниже]
[d1 или d2]
и время жизни d1 или d2 совпадало с текущим скоупом.

Как-то можно этого добиться? Не хочется писать
if true
  d1().f();
else
  d2().f();

а создавать без повода объекты на хипе тоже как-то не круто.
Отредактировано 21.12.2016 18:13 tdiff . Предыдущая версия . Еще …
Отредактировано 21.12.2016 18:12 tdiff . Предыдущая версия .
Отредактировано 21.12.2016 18:11 tdiff . Предыдущая версия .
Re: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 21.12.16 18:45
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Как-то можно этого добиться? Не хочется писать

T>
T>if true
T>  d1().f();
T>else
T>  d2().f();
T>


Как вариант можно вот так:
[&](base &&x)
{
    x.f();
}(true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));

Или вот так:
auto f = [&](auto &&x)
{
    x.f();
};
if(true) f(d1()); else f(d2());
Re[3]: ternary для разных подклассов на стеке
От: antropolog  
Дата: 21.12.16 19:11
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Здесь будет dangling reference.


да, спасибо, разобрался, проблема в самом касте, и ссылка инициализируется не rvalue а ссылкой
Отредактировано 21.12.2016 19:20 antropolog . Предыдущая версия .
Re[4]: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 21.12.16 19:32
Оценка:
Здравствуйте, antropolog, Вы писали:

A>да, спасибо, разобрался, проблема в самом касте, и ссылка инициализируется не rvalue а ссылкой


Дело не в rvalue. Время жизни результата d1() — выражение, грубо говоря до точки с запятой ;. И только в исключительных случаях (специально оговоренных в стандарте) может быть продлено до конца блока, например
const base &x = d1();
Re[2]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 22.12.16 10:07
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Как вариант можно вот так:

EP>
EP>[&](base &&x)
EP>{
EP>    x.f();
EP>}(true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
EP>

Не совсем понимаю, в чём тут разница с
base && x = (true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
x.f() ?
Re[3]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 22.12.16 11:04
Оценка:
tdiff:

EP>>Как вариант можно вот так:

EP>>
EP>>[&](base &&x)
EP>>{
EP>>    x.f();
EP>>}(true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
EP>>

T>Не совсем понимаю, в чём тут разница с
T>
T>base && x = (true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
T>x.f() ?
T>

Разница в том, что во втором случае к моменту вызова f ссылка x указывает на мёртвый объект.

Я вот тут решил другой трюк провернуть

#include <iostream>

struct B
{
    virtual ~B() { std::cout << "~B()" << std::endl; }
    virtual void f() = 0;
};

struct D1 : B
{
    D1() { std::cout << "D1()" << std::endl; }
    ~D1() { std::cout << "~D1()" << std::endl; }
    void f() { std::cout << "D1::f" << std::endl; }
};

struct D2 : B
{
    D2() { std::cout << "D2()" << std::endl; }
    ~D2() { std::cout << "~D2()" << std::endl; }
    void f() { std::cout << "D2::f" << std::endl; }
};

int main()
{
    struct BRef
    {
        B &ref;
    };
    struct D1Wrapper
    {
        D1Wrapper() : value(), ref{value} {}
        D1 value;
        BRef ref;
    };
    struct D2Wrapper
    {
        D2Wrapper() : value(), ref{value} {}
        D2 value;
        BRef ref;
    };
    auto &&bref_1 = true ? D1Wrapper().ref : D2Wrapper().ref;
    auto &&bref_2 = false ? D1Wrapper().ref : D2Wrapper().ref;
    bref_1.ref.f();
    bref_2.ref.f();
}


и не поверил своим глазам: MSVC++ рвёт сразу G++ и Clang++ по части соответствия стандарту: первый компилирует пример корректно, другие два — нет. Кто б мог подумать, а?
Re[4]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 11:14
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Разница в том, что во втором случае к моменту вызова f ссылка x указывает на мёртвый объект.


NI>Я вот тут решил другой трюк провернуть


NI>
NI>int main()
NI>{
NI>    struct BRef
NI>    {
NI>        B &ref;
NI>    };
NI>    struct D1Wrapper
NI>    {
NI>        D1Wrapper() : value(), ref{value} {}
NI>        D1 value;
NI>        BRef ref;
NI>    };
NI>    struct D2Wrapper
NI>    {
NI>        D2Wrapper() : value(), ref{value} {}
NI>        D2 value;
NI>        BRef ref;
NI>    };
NI>    auto &&bref_1 = true ? D1Wrapper().ref : D2Wrapper().ref;
NI>    auto &&bref_2 = false ? D1Wrapper().ref : D2Wrapper().ref;
NI>    bref_1.ref.f();
NI>    bref_2.ref.f();
NI>}


NI>и не поверил своим глазам: MSVC++ рвёт сразу G++ и Clang++ по части соответствия стандарту: первый компилирует пример корректно, другие два — нет. Кто б мог подумать, а?


м... я так понимаю, очевидное предположение что bref_1.ref.f() -- это вызов функции удаленного объекта не соответствует действительности?
Отредактировано 22.12.2016 11:17 night beast . Предыдущая версия .
Re[5]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 22.12.16 11:30
Оценка:
night beast:

NB>м... я так понимаю, очевидное предположение что bref_1.ref.f() -- это вызов функции удаленного объекта не соответствует действительности?


Я там несколько слукавил насчёт "стандарта" — C++17 всё-таки ещё не вышел По C++14 тернарный оператор может создать копию выбранного результата, и тогда да, bref_1.ref.f() может быть вызовом функции для удалённого объекта.
Re[6]: ternary для разных подклассов на стеке
От: Кодт Россия  
Дата: 22.12.16 11:39
Оценка:
Здравствуйте, antropolog, Вы писали:

EP>>const base &x = d1();

A>ну собственно меня и заинтересовало чем принципиально тернарный оператор здесь отличается, потому как например в случае с const base& x = true ? d1() : d1(); провисшей ссылки не будет.

Здесь будет следующее:
— тип обеих веток одинаков, вопросов о приведении не будет
— тернарный оператор возвращает значение
— это значение попадает в скрытый объект типа d1
— ссылка продлевает время его жизни

А тебе нужна логика вот примерно такая
// продлеватели жизни на все случаи
std::optional<d1> tmp1;
std::optional<d2> tmp2;

// для любых выражений, возвращающих объекты по значению
const base& x = condition ? (const base&)(tmp1 = return_d1_byval()).value()
                          : (const base&)(tmp2 = return_d2_byval()).value() ;

// для конструкторов
const base& x = condition ? (tmp1.emplace(12,34), (const base&)tmp1.value())
                          : (tmp2.emplace("abc"), (const base&)tmp2.value()) ;


std::optional есть в C++17, а до того — есть boost::optional

Ну и на самом деле, два optional — это один variant.
Перекуём баги на фичи!
Re[7]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 11:50
Оценка:
Здравствуйте, Кодт, Вы писали:

К>std::optional есть в C++17, а до того — есть boost::optional


раз пошла такая пьянка, то и по optional спрошу.
какие-нибудь ограничения на размер или хранимые значения в нем есть?
а то не очень бы хотелось чтобы он для int занимал в два раза больше памяти чем нужно...
Re[8]: ternary для разных подклассов на стеке
От: Кодт Россия  
Дата: 22.12.16 12:33
Оценка:
Здравствуйте, night beast, Вы писали:

NB>раз пошла такая пьянка, то и по optional спрошу.

NB>какие-нибудь ограничения на размер или хранимые значения в нем есть?
NB>а то не очень бы хотелось чтобы он для int занимал в два раза больше памяти чем нужно...

Ну а куда ты вынесешь один бит признака "есть данные — нет данных"?
Экономить можно только в том случае, когда объект optional не независимый, а в составе, ну хотя бы, вектора.
template<class T, size_t N>
class optional_array {
  bitset<N> present;

  using memory = aligned_storage<sizeof(T), alignof(T)>::type;
  array<memory, N> data;

  const T& peek(size_t i) const {
    return reinterpret_cast<const T&>(data[i]);
  }
  T& peek(size_t i) {
    return reinterpret_cast<const T&>(data[i]);
  }
public:
  T* get(size_t i) {
    assert(i < N);
    return present[i] ? &peek(i) : nullptr;
  }
  void set(size_t i, const T& v) {  // и то же самое для T&& и для emplace.
    if (present[i])
      peek(i) = v;
    else {
      new(&peek(i)) T(v);
      present[i] = true;
    }
  }
  void reset(size_t i) {
    if (present[i]) {
      peek(i).~T();
      present[i] = false;
    }
  }

  ~optional_array() {
    for(size_t i = 0; i < N; ++i) reset(i);
  }
  optional_array(const optional_array& arr) {  // и то же самое для &&
    for(size_t i = 0; i < N; ++i) if(arr.get(i)) set(arr.get(i));
  }
};
Перекуём баги на фичи!
Re[8]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 12:40
Оценка:
Здравствуйте, N. I., Вы писали:

NB>>блин. как-так.

NB>>как D1Wrapper() может пережить конец выражения если напрямую нигде не используется?

NI>Раньше было так: когда инициализируешь такую ссылку частью объекта (представленного prvalue выражением), то время жизни всего объекта целиком подлежит продлению:

NI>

The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except: [...]



спасиб. наверно, пока такие трюки опасно применять...
Re[9]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 12:45
Оценка:
Здравствуйте, Кодт, Вы писали:

NB>>раз пошла такая пьянка, то и по optional спрошу.

NB>>какие-нибудь ограничения на размер или хранимые значения в нем есть?
NB>>а то не очень бы хотелось чтобы он для int занимал в два раза больше памяти чем нужно...

К>Ну а куда ты вынесешь один бит признака "есть данные — нет данных"?


у себя в велосипеде я использую особые значения вроде INT_MIN (конкретная стратегия применят флаг или некоторое значение задается в треитсах)
Отредактировано 22.12.2016 12:54 night beast . Предыдущая версия .
Re[4]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 22.12.16 13:01
Оценка:
Здравствуйте, N. I., Вы писали:

T>>Не совсем понимаю, в чём тут разница с

T>>
T>>base && x = (true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
T>>x.f() ?
T>>


NI>Разница в том, что во втором случае к моменту вызова f ссылка x указывает на мёртвый объект.


Почему, если base&& должна продлевать время жизни temporary?

У меня есть догадка, что происходит что-то вроде:
1. создаётся temporary d1() с типом d1&&
2. тип этого объекта конвертируется в base&&
3. результатом ternary является base&&, которая продлевает время жизни d1
3. этой ссылкой инициализируется ссылка base&& x, которая почему-то уже не продлевает жизни d1

Это, в частности, подтверждается cppref:

In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.

Правильно ли я понимаю, что вы это имеете в виду?
Re[10]: ternary для разных подклассов на стеке
От: PM  
Дата: 22.12.16 13:45
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, Кодт, Вы писали:


NB>>>раз пошла такая пьянка, то и по optional спрошу.

NB>>>какие-нибудь ограничения на размер или хранимые значения в нем есть?
NB>>>а то не очень бы хотелось чтобы он для int занимал в два раза больше памяти чем нужно...

К>>Ну а куда ты вынесешь один бит признака "есть данные — нет данных"?


NB>у себя в велосипеде я использую особые значения вроде INT_MIN (конкретная стратегия применят флаг или некоторое значение задается в треитсах)


Типа такого: https://github.com/akrzemi1/compact_optional ? Вроде бы требования довольно распространённые, реализация несложная. Странно, что в std::optional не включили traits. Похоже, просто не нашлось желающих сделать соответствующее предложение.
Re[11]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 14:06
Оценка:
Здравствуйте, PM, Вы писали:

К>>>Ну а куда ты вынесешь один бит признака "есть данные — нет данных"?


NB>>у себя в велосипеде я использую особые значения вроде INT_MIN (конкретная стратегия применят флаг или некоторое значение задается в треитсах)


PM>Типа такого: https://github.com/akrzemi1/compact_optional ? Вроде бы требования довольно распространённые, реализация несложная.


реализация другая, но смысл такой.

PM>Странно, что в std::optional не включили traits. Похоже, просто не нашлось желающих сделать соответствующее предложение.


тут проблема в том что треитсы (или таги) придется или вторым шаблонным параметром передавать,
или вместо optional< int > использовать optional< some_strategy_tag<int> > (some_strategy_tag используется только для недефолтовых треитсов)
возможно решили что оно того не стоит.
Отредактировано 22.12.2016 14:07 night beast . Предыдущая версия .
Re[12]: ternary для разных подклассов на стеке
От: PM  
Дата: 22.12.16 18:58
Оценка:
Здравствуйте, night beast, Вы писали:

PM>>Странно, что в std::optional не включили traits. Похоже, просто не нашлось желающих сделать соответствующее предложение.


NB>тут проблема в том что треитсы (или таги) придется или вторым шаблонным параметром передавать,

NB>или вместо optional< int > использовать optional< some_strategy_tag<int> > (some_strategy_tag используется только для недефолтовых треитсов)
NB>возможно решили что оно того не стоит.

Насколько я понял из обсуждения https://groups.google.com/a/isocpp.org/forum/m/#!topic/std-proposals/46J1onhWJ-s/discussion дополнительная стратегия для optional может поменять его семантику, что привело бы к существенным изменениям в существующем std::optional, так что дальше обсуждения дело не пошло.
Re[13]: ternary для разных подклассов на стеке
От: night beast СССР  
Дата: 22.12.16 20:02
Оценка:
Здравствуйте, PM, Вы писали:

NB>>тут проблема в том что треитсы (или таги) придется или вторым шаблонным параметром передавать,

NB>>или вместо optional< int > использовать optional< some_strategy_tag<int> > (some_strategy_tag используется только для недефолтовых треитсов)
NB>>возможно решили что оно того не стоит.

PM>Насколько я понял из обсуждения https://groups.google.com/a/isocpp.org/forum/m/#!topic/std-proposals/46J1onhWJ-s/discussion дополнительная стратегия для optional может поменять его семантику, что привело бы к существенным изменениям в существующем std::optional, так что дальше обсуждения дело не пошло.


как я понял, предлагали еще в 12г (когда не было std::optional), но походу, забили на это дело ))
Re[3]: ternary для разных подклассов на стеке
От: Evgeny.Panasyuk Россия  
Дата: 22.12.16 20:41
Оценка:
Здравствуйте, tdiff, Вы писали:

EP>>Как вариант можно вот так:

EP>>
EP>>[&](base &&x)
EP>>{
EP>>    x.f();
EP>>}(true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
EP>>

T>Не совсем понимаю, в чём тут разница с
T>
T>base && x = (true ? static_cast<base&&>(d1()) : static_cast<base&&>(d2()));
T>x.f() ?
T>


Разница в том, что в варианте с вызовом лямбды (или даже простой функции) — это всё одно выражение (expression), и есть гарантия что временные объекты живут до конца выполнения выражения в котором были созданы.
Во втором же случае, потребовалась бы продление времени жизни временной переменной до конца scope — что в данном случае нетривиально, так как потребовало бы от компилятора запоминание того какого типа значение в итоге забиндилось, сколько оно занимает на стэке, и в конце scope в зависимости от варианта — d1 или d2 — вызывать правильный деструктор и правильно очищать стэк.
Re[4]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 23.12.16 08:28
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Во втором же случае, потребовалась бы продление времени жизни временной переменной до конца scope


Спасибо за подробный ответ, но по-моему у нас есть гарантия, что const & / && — ссылки продлевают жизнь временным объектам.
Re: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 23.12.16 10:05
Оценка:
Здравствуйте, tdiff, Вы писали:

T>хочу, чтобы стек выглядел вот так:

T>[ссылка на объект ниже]
T>[d1 или d2]
T>и время жизни d1 или d2 совпадало с текущим скоупом.

Что мешает написать то, что ты хочешь, прямо?
struct base {
    virtual ~base() {}
    virtual void f() = 0;
};

struct d1 : base { long long x; void f() { x++; } };
struct d2 : base { static int x; void f() { x++; } };

class CBasePlaceholder {
public:
    CBasePlaceholder() : ptr(0) {}
    CBasePlaceholder(const CBasePlaceholder&) = delete;
    CBasePlaceholder(CBasePlaceholder&&) = delete;
    ~CBasePlaceholder() { Destroy(); }

    base* Ptr() const { return ptr; }
    base* operator ->() const { assert(ptr != 0); return ptr; }

    template<typename T> base* New() { Destroy(); return ::new(&bodyStart) T(); }

    void Destroy()
    {
        if (base* toDelete = ptr) {
            ptr = 0;
            toDelete->~base();
        }
    }
private:
    typedef double tAlignmentAs;
    enum {
        firstSize = sizeof(d1),
        secondSize = sizeof(d2),
        bufferSize = firstSize > secondSize ? firstSize : secondSize
    };
    tAlignmentAs bodyStart;
    char bodyRest[bufferSize - sizeof(tAlignmentAs)];
    base* ptr;

};

void foo(bool f)
{
    CBasePlaceholder t;
    base& rt = *(f ? t.New<d1>() : t.New<d2>());
    rt.f();
}


Шаблоны, вариадики и обработку случая, когда максимальный рзмер буфера меньше или равен sizeof(tAlignmentAs) добавить по вкусу

А вообще-то, писать так как тебе не хочется проще и понятнее всего
За одно ещё и от динамического полиморфизма избавишься
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 23.12.16 14:21
Оценка:
Erop:

E>Что мешает написать то, что ты хочешь, прямо?


Много букв и error prone, если не знать, как правильно создавать и удалять вручную объекты с правильным соблюдением всех па.

E>Шаблоны, вариадики и обработку случая, когда максимальный рзмер буфера меньше или равен sizeof(tAlignmentAs) добавить по вкусу


Если есть alignas/alignof, то лучше сразу использовать их: http://melpon.org/wandbox/permlink/sHVUIR2vV9aBtrUc

Другое дело, что вряд ли всё это вообще нужно. Обычно такие объекты или редко создаются или сами по себе настолько тяжелы, что подобная "оптимизация" реально ничего заметного не даст.
Re[6]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 23.12.16 14:54
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Всё дело в мутном понятии "temporary". Под ним подразумеваются сразу и временный объект и выражение, которое обозначает этот объект. Выражению, которое мы используем в качестве инициализатора ссылки на временный объект, недостаточно просто ссылаться на данный объект, чтоб его время жизни продлялось, — само выражение должно быть "temporary" (если б продление времени жизни временного объекта работало для любого выражения-инициализатора, то как минимум возникли бы очевидные сложности с проверкой, является ли объект временным).

NI> ...
NI> Выражение является temporary только если в правилах для него явно указано, что оно temporary, либо если оно непосредственно создаёт и возвращает некий temporary object
NI> ...
NI>Формально там prvalue типа d1 преобразуется к xvalue типа base. Исходное prvalue является temporary, а получившееся xvalue уже не является temporary (хотя оно тоже ссылается на временный объект).
NI> ...
NI>x инициализируется non-temporary выражением, поэтому продление не применяется.

Спасибо за подробный ответ. Это действительно довольно мутно Например, почему "получившееся xvalue уже не является temporary"? Беглый поиск "temporary expression" по стандарту вообще находит только одно упоминание.
Re: ternary для разных подклассов на стеке
От: kov_serg Россия  
Дата: 23.12.16 14:56
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Привет


T>Хочется в зависимости от условия создавать на стеке объекты разных подтипов одного общего класса, но пока не получается:

T>
T>class base { virtual void f() const = 0;};
T>class d1 : public base { void f() const override {...} };
T>class d2 : public base { void f() const override {...} };

T>const base& obj = true ? (const base&)d1() : d2();
T>obj.f();
T>

...
T>а создавать без повода объекты на хипе тоже как-то не круто.

А зачем тебе вообще объекты?
Re[3]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 23.12.16 19:32
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Много букв и error prone, если не знать, как правильно создавать и удалять вручную объекты с правильным соблюдением всех па.

Конкретно этот трудно довольно неправильно использовать, если ещё и operator = удалить.

NI>Другое дело, что вряд ли всё это вообще нужно. Обычно такие объекты или редко создаются или сами по себе настолько тяжелы, что подобная "оптимизация" реально ничего заметного не даст.


Казалось бы, если тяжелы, то стека в два раза меньше съестся...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 23.12.16 19:35
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Видимо желание узнать о готовом решении а-ля boost/std::variant превалирует над желанием лисапедить


Я так понимаю, что всё это под флагом борьбы с оверхедом же?
Конечно подозрительная такая бескомпромиссная борьба, но фиг его знает-то, может людям реально надо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 23.12.16 20:07
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>А зачем тебе вообще объекты?


Ну как обычно, ради состояния
Re[3]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 23.12.16 20:28
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Ну как обычно, ради состояния


А динамический полиморфизм для чего?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 23.12.16 21:20
Оценка:
Erop:

E>Конкретно этот трудно довольно неправильно использовать


С выравниванием там не всё гладко. У long double и __int128 оно может быть больше, чем у double. А увеличение выравнивания (и, соответственно, размера) tAlignmentAs приведёт к увеличению вероятности того, что разность bufferSize — sizeof(tAlignmentAs) однажды посчитается не так, как того хотелось бы. Вместо этих плясок с вычислением размеров проще было бы union использовать:

union aligned_storage_t
{
    max_align_t alignment_object;
    unsigned char d1_storage[sizeof(d1)];
    unsigned char d2_storage[sizeof(d2)];
} aligned_storage;


NI>>Другое дело, что вряд ли всё это вообще нужно. Обычно такие объекты или редко создаются или сами по себе настолько тяжелы, что подобная "оптимизация" реально ничего заметного не даст.


E>Казалось бы, если тяжелы, то стека в два раза меньше съестся...


Если их в динамической памяти создавать, то стека много не понадобится.
Re[5]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 23.12.16 21:56
Оценка:
Здравствуйте, N. I., Вы писали:


NI>С выравниванием там не всё гладко. У long double и __int128 оно может быть больше, чем у double. А увеличение выравнивания (и, соответственно, размера) tAlignmentAs приведёт к увеличению вероятности того, что разность bufferSize — sizeof(tAlignmentAs) однажды посчитается не так, как того хотелось бы.


Ну это же не шаблон, а демонстрация подхода.
Если делать шаблон, можно аккуратно выбирать
Например, получать выравнивание d1 и d2 из чего-то вроде
template<typename T> class AligOf {
    struct S {
        T t;
        char c;
    };
public:
    enum { Value = sizeof( S ) - sizeof( T ) };
};

Потом метафункцией max получать большее из двух, и потом по значению параметра подбирать tAlignmentAs.
Это если по умному делать. А если не по-умному, то можно сделать tAlignmentAs параметром шаблона со значением double или там long long по умолчанию

Что касается "плясок", то если учесть, что там ещё указатель на виртуальную таблицу должен быть по прихотливой задумке ТС, то получается, что размер остатка буфера не может быть отрицательным, при верном подборе tAlignmentAs, и не может быть равным 0, если в d1 или d2 есть какие-то свои поля.

NI>Вместо этих плясок с вычислением размеров проще было бы union использовать:

NI>
union aligned_storage_t
NI>{
NI>    max_align_t alignment_object;
NI>    unsigned char d1_storage[sizeof(d1)];
NI>    unsigned char d2_storage[sizeof(d2)];
NI>} aligned_storage;


Так хуже, так как может занять лишнюю память.
Представь себе, что sizeof( d1 )равен 40, при выравнивании 8, а выравнивание d2 равно 16, при размере 32...

Если вообще, ну теоретически, рассматривать, что эта нужда возникает часто и вместо велика по случаю строгать библиотечный класс, то я думаю, что выноса tAlignmentAs в параметры шаблона было бы достаточно. Если что-то пойдёт не так, что размер массива получится <= 0 и компилятор выругается

NI>Если их в динамической памяти создавать, то стека много не понадобится.


Динамическая память может быть дорогой по времени...


В сухом остатке выходит что-то вроде
template<size_t TBufferSize, typename TBase = base, typename TAlligAs = long long >
class CPlaceHolder {
// Всё, как раньше, но добавляем в New проверки

    template<typename T> TBase* New() 
    {
        typedef char assert_corerct_alig[sizeof( TAlligAs ) - AligOf<T>::Value +1];
        typedef char assert_correct_size[TBufferSize - sizeof( T ) + 1];
        Destroy();
        return ptr = ::new( &bodyStart ) T;
    }


};
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: ternary для разных подклассов на стеке
От: cserg  
Дата: 23.12.16 23:23
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Хочется в зависимости от условия создавать на стеке объекты разных подтипов одного общего класса, но пока не получается:

В llvm есть шаблонный класс AlignedCharArrayUnion, представляет собой выровненный массив символов, способный вместить объекты типов указанные в параметрах шаблона. Можно выпилить его оттуда (см. AlignOf.h), тогда получим что-то вроде этого:
AlignedCharArrayUnion<d1, d2> d;
const base* pd = true ? (base*) new (d.buffer) d1() : (base*) new (d.buffer) d2();
pd->f();
// ...
pd->~base();

Деструктор base не забудьте сделать виртуальным, d.buffer лучше обернуть в reinterpret_cast, я для краткости опустил.
Re[3]: ternary для разных подклассов на стеке
От: kov_serg Россия  
Дата: 24.12.16 08:52
Оценка:
Здравствуйте, tdiff, Вы писали:

T>Здравствуйте, kov_serg, Вы писали:


_>>А зачем тебе вообще объекты?


T>Ну как обычно, ради состояния


А кратко описать проблему котарая решается нельзя?

Вы хотите добиться подобного поведения только иными путями?
struct base { virtual void f() const = 0;};
struct d1 : base { void f() const {} };
struct d2 : base { void f() const {} };

void f0(const base& d) { d.f(); }
void f1() { d1 d; f0(d); }
void f2() { d2 d; f0(d); }
void (*fn[])()={f1,f2};

void action(int idx) {
  fn[idx&1]();
  if (idx&1) f0(d2()); else f0(d1());
}
Re[6]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 24.12.16 12:36
Оценка:
Erop:

E>А если не по-умному, то можно сделать tAlignmentAs параметром шаблона со значением double или там long long по умолчанию


И потом следить за тем, из чего там состоят d1 и d2 (прощай, инкапсуляция)? Единственным безопасным типом по умолчанию там может быть только тип с максимально возможным выравниванием.

E>Что касается "плясок", то если учесть, что там ещё указатель на виртуальную таблицу должен быть по прихотливой задумке ТС, то получается, что размер остатка буфера не может быть отрицательным, при верном подборе tAlignmentAs, и не может быть равным 0, если в d1 или d2 есть какие-то свои поля.


Отрицательным размер не может быть из-за беззнакового вычитания (вместо отрицального числа там было бы очень большое). Нулевой размер может получиться, например, так: засунули мы в один из производных классов long double, и получили требование выравнивать адрес на 16 байт. Сделали тип tAlignmentAs 16-байтным. Потом все производные классы внезапно переделали с применением pimpl. Получили классы, где 8 байт используется под указатель на vtable и ещё 8 — под указатель pimpl. Итого 16 против 16.

E>Так хуже, так как может занять лишнюю память.


Есть такое, но и применение безопасного max_align_t изначально не подразумевает оптимальное использование памяти в общем случае. Ну, и возникает вопрос, сколько экземпляров такого хелпера надо одновременно создать, чтоб получить ощутимую разницу. В принципе, если сильно припёрло, память можно сэкономить за счёт использования анонимного объединения:

union
{
    alignment_type<max_value<align_of<d1>::value, align_of<d2>::value>::value>::type alignment_object;
    struct
    {
        unsigned char storage[max_value<sizeof(d1), sizeof(d2)>::value];
        base* ptr;
    } data;
};

Но, опять же, такие трюки может оправдать только отсутствие поддержки alignas или его аналогов компилятором.

E>Динамическая память может быть дорогой по времени...


Может или не может в конкретном случае, это ещё надо выяснить, прежде чем браться за такие "оптимизации".
Отредактировано 24.12.2016 12:40 N. I. . Предыдущая версия .
Re[7]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 24.12.16 13:19
Оценка:
Здравствуйте, N. I., Вы писали:

NI>И потом следить за тем, из чего там состоят d1 и d2 (прощай, инкапсуляция)? Единственным безопасным типом по умолчанию там может быть только тип с максимально возможным выравниванием.


Если хочется "по умному", то можно же и по умному сделать.
Просто в большинстве случаев это не нужно.

От задач, конечно, зависит, но обычно типы с выравниваем больше 8 байт -- редкость
И если он в предметной области не редкость, то там и так всё с ними понятно обычно (числодробилки векторыне, например).
Кроме того, можно написать assert, например такой:
    struct testAligAs {
        tAligAs a;
        T t;
    };
    typedef char assert_alig_as_size_is_enough[(sizeof(tAligAs)+sizeof(T)-sizeof(testAligAs))*2 + 1];


NI>Отрицательным размер не может быть из-за беззнакового вычитания (вместо отрицального числа там было бы очень большое).

С чего бы это член enum стал бы беззнаковым?

NI>Нулевой размер может получиться, например, так: засунули мы в один из производных классов long double, и получили требование выравнивать адрес на 16 байт. Сделали тип tAlignmentAs 16-байтным. Потом все производные классы внезапно переделали с применением pimpl. Получили классы, где 8 байт используется под указатель на vtable и ещё 8 — под указатель pimpl. Итого 16 против 16.


Ну это, само по себе, противоречит идее избавится от кучи...
Так что в этом случае, скорее хорошо, если не скомпилируется и надо будет посмотреть что случилось...
Но, опять же, если ОЧЕНЬ хочется, то можно это всё предусмотреть.
Можно тут часть, где объявляются поля вынести в отдельную структуру шаблонную, от трёх параметров. typename tAligAs, int restSize, typename TBase. Ну и специализировать её на случай 0 размера

E>>Так хуже, так как может занять лишнюю память.


NI>Есть такое, но и применение безопасного max_align_t изначально не подразумевает оптимальное использование памяти в общем случае.

Так и его не надо применять

NI>Ну, и возникает вопрос, сколько экземпляров такого хелпера надо одновременно создать, чтоб получить ощутимую разницу. В принципе, если сильно припёрло, память можно сэкономить за счёт использования анонимного объединения:


Ты, наверное, хотел сказать "анонимной структуры"?
Но, в целом это не важно, можно и объединение и структуру сделать вполне себе именованными. Это ничего не меняет.
Так тоже можно, но если писать, как было с начала, то во всяких неочевидных случаях не скомпилируется, и программисту придётся разобраться, какие предположения о d1 и d2 он нарушил...

NI>Но, опять же, такие трюки может оправдать только отсутствие поддержки alignas или его аналогов компилятором.

Если есть современный C++, то правильно захватить всё нужное в лямбду с auto и вызвать её, в зависимости от условия, от d1() или d2()...
Можно даже и из ternary, если очень хочется...


E>>Динамическая память может быть дорогой по времени...


NI>Может или не может в конкретном случае, это ещё надо выяснить, прежде чем браться за такие "оптимизации".


+100500
Я в самом первом сообщении написал, что так делать вообще не надо.
Я, кстати, задал там же вопрос, от чего бы не сделать ровно то, чего хочется, если уж так припёрло...
И примерно показал как.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 25.12.16 10:13
Оценка:
Erop:

E>Если хочется "по умному", то можно же и по умному сделать.

E>Просто в большинстве случаев это не нужно.

Ну, по мне, если уж велосипедить, то так, чтоб потом не было сомнений по поводу надёжности

NI>>Отрицательным размер не может быть из-за беззнакового вычитания (вместо отрицального числа там было бы очень большое).

E>С чего бы это член enum стал бы беззнаковым?

Там он может быть как знаковым, так и беззнаковым, но размер std::size_t (результат sizeof) практически гарантированно не меньше. Если underlying type того перечисления знаковый, то все возможные значения std::size_t туда не влезут, а, стало быть, и общий тип будет std::size_t, т.е. беззнаковый.

NI>>Нулевой размер может получиться, например, так: засунули мы в один из производных классов long double, и получили требование выравнивать адрес на 16 байт. Сделали тип tAlignmentAs 16-байтным. Потом все производные классы внезапно переделали с применением pimpl. Получили классы, где 8 байт используется под указатель на vtable и ещё 8 — под указатель pimpl. Итого 16 против 16.


E>Ну это, само по себе, противоречит идее избавится от кучи...


А, может, там какой-то быстрый пул памяти используется

E>Так что в этом случае, скорее хорошо, если не скомпилируется и надо будет посмотреть что случилось...


Угу, посмотрели — выяснили, что велосипед не работает и надо тратить время или на починку велосипеда, или на замену его чем-то другим

E>Но, опять же, если ОЧЕНЬ хочется, то можно это всё предусмотреть.

E>Можно тут часть, где объявляются поля вынести в отдельную структуру шаблонную, от трёх параметров. typename tAligAs, int restSize, typename TBase. Ну и специализировать её на случай 0 размера

Через union определённо проще получается

NI>>Ну, и возникает вопрос, сколько экземпляров такого хелпера надо одновременно создать, чтоб получить ощутимую разницу. В принципе, если сильно припёрло, память можно сэкономить за счёт использования анонимного объединения:


E>Ты, наверное, хотел сказать "анонимной структуры"?


Неа, я имел в виду именно anonymous union, к мемберам которого можно обращаться, как к мемберам объемлющего класса.

E>Если есть современный C++, то правильно захватить всё нужное в лямбду с auto и вызвать её, в зависимости от условия, от d1() или d2()...


Тоже нагромождение, но с другого конца К тому же там есть свои ограничения.
Re[9]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 25.12.16 10:23
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Ну, по мне, если уж велосипедить, то так, чтоб потом не было сомнений по поводу надёжности

Так простой код с asserts надёжнее же

E>>С чего бы это член enum стал бы беззнаковым?


NI>Там он может быть как знаковым, так и беззнаковым, но размер std::size_t (результат sizeof) практически гарантированно не меньше. Если underlying type того перечисления знаковый, то все возможные значения std::size_t туда не влезут, а, стало быть, и общий тип будет std::size_t, т.е. беззнаковый.

Чушь, тип enum определяется набором КОНСТАНТ, что бы туда не влез размер, надо что бы размер d1 или d2 превышал половину адресного пространства...
Я думаю, что тогда это всё равно не скомпилируется или не запустится, не тут, так где-то ещё...
IMHO, решать надо реальные проблемы, а не выдуманные...
Но если это тебя так беспокоит, можно явно перенести все вычисления в отрицательные числа. Оперировать не с размерами, а с "минус размерами"

NI>А, может, там какой-то быстрый пул памяти используется

Ну тем более, зачем тогда это всё городить, если быстрый?
По уму тогда от этого вела надо отказаться.

Ну и не понятно ещё, зачем не сделать на pimpl сразу base, а base превратить в NVI...

NI>Угу, посмотрели — выяснили, что велосипед не работает и надо тратить время или на починку велосипеда, или на замену его чем-то другим

Так таки и надо же.

NI>Неа, я имел в виду именно anonymous union, к мемберам которого можно обращаться, как к мемберам объемлющего класса.

Так же можно и со структурой. Но это не важно тут. Всё равно можно не использовать.

NI>Тоже нагромождение, но с другого конца

А в чём нагромождение? Всё сразу прямо и на языке, а не на библиотеке велов написано.

NI>К тому же там есть свои ограничения.

Какие?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 25.12.16 12:12
Оценка:
Здравствуйте, Erop, Вы писали:

NI>>Ну, по мне, если уж велосипедить, то так, чтоб потом не было сомнений по поводу надёжности

E>Так простой код с asserts надёжнее же

Ни фига се, простой

NI>>Там он может быть как знаковым, так и беззнаковым, но размер std::size_t (результат sizeof) практически гарантированно не меньше. Если underlying type того перечисления знаковый, то все возможные значения std::size_t туда не влезут, а, стало быть, и общий тип будет std::size_t, т.е. беззнаковый.

E>Чушь, тип enum определяется набором КОНСТАНТ, что бы туда не влез размер, надо что бы размер d1 или d2 превышал половину адресного пространства...

Вот из-за того, что реальные величины sizeof(d1) и size(d2) довольно малы, они легко уместятся в int. Если все элементы перечисления умещаются в int, то underlying type перечисления не может быть больше int.

Теперь давай разберём выражение X — Y. Если один из операндов имеет тип std::size_t, а другой имеет некий знаковый целый тип T, то общим типом (который станет типом результата разности) будет либо std::size_t, либо T, либо promoted вариант одного из этих типов (promotion делается до int или unsigned int), либо соответствующий T беззнаковый тип. Для того, чтобы это был T, надо, чтобы T был не меньше int и вмещал в себя все возможные значения std::size_t (такое может быть, например, когда std::size_t эквивалентен std::uint32_t, а T — это std::int64_t). Нет практически никаких шансов встретить реализацию, где int вмещал бы в себя все значения std::size_t. Так что тут один и из тех случаев, когда разность знакового и беззнакового значений будет подсчитана как беззнаковая величина.

NI>>А, может, там какой-то быстрый пул памяти используется

E>Ну тем более, зачем тогда это всё городить, если быстрый?

Уже нагородили раньше.

E>По уму тогда от этого вела надо отказаться.


Если код корректно работает, зачем его трогать?

E>Ну и не понятно ещё, зачем не сделать на pimpl сразу base, а base превратить в NVI...


Это из разряда "а почему бы с первого раза не сделать всё идеально?". Ну, вот не всегда так получается.

NI>>Угу, посмотрели — выяснили, что велосипед не работает и надо тратить время или на починку велосипеда, или на замену его чем-то другим

E>Так таки и надо же.

Если бы код продолжал корректно работать, то время можно было бы потратить на что-то другое.

NI>>Неа, я имел в виду именно anonymous union, к мемберам которого можно обращаться, как к мемберам объемлющего класса.

E>Так же можно и со структурой.

Только в качестве расширения (которое, впрочем, многими компиляторами C++ поддерживается).

E>Но это не важно тут. Всё равно можно не использовать.


Можно (на обратном я, вроде бы, не настаивал )

NI>>Тоже нагромождение, но с другого конца

E>А в чём нагромождение?

Появление функции внутри функции — это типа не нагромождение? А если таких внутренних функций ещё и не одна?

NI>>К тому же там есть свои ограничения.

E>Какие?

Попробуй сделать break или continue изнутри этой лямбды. Или, например, вернуть неперемещаемый объект, не имея доступа к компилятору с поддержкой guaranteed copy elision из C++17.
Отредактировано 25.12.2016 21:26 N. I. . Предыдущая версия .
Re[12]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 25.12.16 19:40
Оценка:
Erop:

E>Разность знакового и беззнакового всегда знаковая...


Понятненько.
Re[6]: ternary для разных подклассов на стеке
От: Кодт Россия  
Дата: 25.12.16 23:02
Оценка:
Здравствуйте, Erop, Вы писали:

NI>>Вместо этих плясок с вычислением размеров проще было бы union использовать:

NI>>
union aligned_storage_t
NI>>{
NI>>    max_align_t alignment_object;
NI>>    unsigned char d1_storage[sizeof(d1)];
NI>>    unsigned char d2_storage[sizeof(d2)];
NI>>} aligned_storage;


Так, между прочим, — http://en.cppreference.com/w/cpp/types/aligned_storage — чтоб не велосипедить то, что уже есть в стандарте...
Перекуём баги на фичи!
Re[7]: ternary для разных подклассов на стеке
От: Erop Россия  
Дата: 25.12.16 23:17
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Так, между прочим, — http://en.cppreference.com/w/cpp/types/aligned_storage — чтоб не велосипедить то, что уже есть в стандарте...


Там, выше, это обсуждалось уже. Если С++ новый, то проще люмбду намутить и звать её от d1() Или от d2()...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 26.12.16 09:49
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>А кратко описать проблему котарая решается нельзя?

_>Вы хотите добиться подобного поведения только иными путями?

Проблемой это нельзя назвать — я хотел добиться того же, что и в вашем примере, но без введения доп. функции, с минимальным и нестрашным (субъективно) синтаксисом.

В других ветках предложили два решения: одно такое же как ваше — передавать временный объект в функцию, продлевая время его жизни на всё выражение, второе — использовать какую-то вариацию на тему variant.
Re[2]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 26.12.16 10:05
Оценка:
Здравствуйте, cserg, Вы писали:

C>Здравствуйте, tdiff, Вы писали:


T>>Хочется в зависимости от условия создавать на стеке объекты разных подтипов одного общего класса, но пока не получается:

C>В llvm есть шаблонный класс AlignedCharArrayUnion, представляет собой выровненный массив символов, способный вместить объекты типов указанные в параметрах шаблона. Можно выпилить его оттуда (см. AlignOf.h), тогда получим что-то вроде этого:
C>
C>AlignedCharArrayUnion<d1, d2> d;
C>const base* pd = true ? (base*) new (d.buffer) d1() : (base*) new (d.buffer) d2();
pd->>f();
C>// ...
pd->>~base();
C>

C>Деструктор base не забудьте сделать виртуальным, d.buffer лучше обернуть в reinterpret_cast, я для краткости опустил.

Спасибо, это интересный вариант, но проще сделать функцию, которая будет принимать временный объект.
Re[8]: ternary для разных подклассов на стеке
От: tdiff  
Дата: 26.12.16 10:24
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Потому что не указано, что оно temporary Вот, смотри:


А можешь подсказать, где указано, какие выражения temporary, а какие нет? Определения "temporary expression" в тексте нет вообще, т.е. там это сформулировано как-то косвенно. Меня это интересует, т.к. для меня было неочевидно, что const base& x = true ? d1() : d2() приведёт к dangling reference, и хотелось бы знать те места, где продление времени жизни ссылкой работает.
Re[6]: ternary для разных подклассов на стеке
От: Meyers  
Дата: 23.03.18 21:39
Оценка:
NI>Выражения никогда не имеют ссылочный тип. Типом выражения d1() будет d1.

Вот прям никогда

3 If the postfix-expression designates a destructor, the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.
Re[4]: ternary для разных подклассов на стеке
От: Meyers  
Дата: 23.03.18 22:10
Оценка:
NI>и не поверил своим глазам: MSVC++ рвёт сразу G++ и Clang++ по части соответствия стандарту: первый компилирует пример корректно, другие два — нет. Кто б мог подумать, а?

Это второе известное мне место, где MSVC++ рвёт g++ и clang++ по части соответствия стандарту.
Re[7]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 24.03.18 08:09
Оценка:
Meyers:

NI>>Выражения никогда не имеют ссылочный тип. Типом выражения d1() будет d1.


M>Вот прям никогда


M>3 If the postfix-expression designates a destructor, the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.


Разбирали уже много раз: the type of the function call expression, о котором там идёт речь, и окончательно установленный тип выражения — это две разные вещи. Окончательно установленный (т.е. фактический) тип выражения получается после всех трансформаций, описанных в [expr]/5 и [expr]/6. Попытка получить выражение ссылочного типа мгновенно превращает его в glvalue нессылочного типа. Все остальные правила стандарта (кроме [expr]/5 и [expr]/6, предназначением которых является установление фактического типа выражения), где описывается какая-либо зависимость от типа выражения, под типом выражения подразумевают именно его окончательно установленный тип (который в силу [expr]/5 ссылочным быть не может никогда).
Re[5]: ternary для разных подклассов на стеке
От: N. I.  
Дата: 24.03.18 08:19
Оценка:
Meyers:

NI>>и не поверил своим глазам: MSVC++ рвёт сразу G++ и Clang++ по части соответствия стандарту: первый компилирует пример корректно, другие два — нет. Кто б мог подумать, а?


M>Это второе известное мне место, где MSVC++ рвёт g++ и clang++ по части соответствия стандарту.


Как выяснилось позже, всё-таки не рвёт.
Re[8]: ternary для разных подклассов на стеке
От: Meyers  
Дата: 24.03.18 16:24
Оценка:
N. I.:

NI>Meyers:


NI>>>Выражения никогда не имеют ссылочный тип. Типом выражения d1() будет d1.


M>>Вот прям никогда


M>>3 If the postfix-expression designates a destructor, the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.


NI>Разбирали уже много раз: the type of the function call expression, о котором там идёт речь, и окончательно установленный тип выражения — это две разные вещи. Окончательно установленный (т.е. фактический) тип выражения получается после всех трансформаций, описанных в [expr]/5 и [expr]/6. Попытка получить выражение ссылочного типа мгновенно превращает его в glvalue нессылочного типа. Все остальные правила стандарта (кроме [expr]/5 и [expr]/6, предназначением которых является установление фактического типа выражения), где описывается какая-либо зависимость от типа выражения, под типом выражения подразумевают именно его окончательно установленный тип (который в силу [expr]/5 ссылочным быть не может никогда).


Ладно, ладно. Я знаю, как это работает.

Но есть известные люди в C++ тусовке, в честь одного из них я назвал этот аккаукн, которые считают, что "Выражения никогда не имеют ссылочный тип" — говорить нельзя, т.к. имеют.

Кстати, некоторое время назад про unqualified-id было сказано, что тип выражения совпадает с типом штуки, которую обозначает unqualified-id. Сегодня посмотрел, а там всё переписали. Такими темпами скоро перепишут [expr.call]/3 и вышеназванному известному персонажу не на что будет ссылаться для обоснования своей т.з. что выражения имеют ссылочный тип.
Re[6]: ternary для разных подклассов на стеке
От: Meyers  
Дата: 24.03.18 16:24
Оценка:
N. I.:

NI>Meyers:


NI>>>и не поверил своим глазам: MSVC++ рвёт сразу G++ и Clang++ по части соответствия стандарту: первый компилирует пример корректно, другие два — нет. Кто б мог подумать, а?


M>>Это второе известное мне место, где MSVC++ рвёт g++ и clang++ по части соответствия стандарту.


NI>Как выяснилось позже, всё-таки не рвёт.


А подробнее?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.