Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 05.09.21 11:56
Оценка:
Возьмем какой-нибудь POD тип T для которого явно не запрещен, но и не определен конструктор по умолчанию. Тогда в объявлении мы можем либо инициализировать тип нулями, либо нет:
T a;    // Нет никакой инициализации
T a();  // Инициализируем нулями
T a{};  // Инициализируем нулями
Теперь допустим, что я хочу определить конструкторы, в том числе и конструктор по умолчанию, но который не должен инициализировать члены класса T, например:
class T
{
    T() {}
    T(const T& that) = default;
    T(T&& that) noexcept = default;
    // ...
}
Теперь если смотреть на все это в контексте какого-нибудь шаблонно метода, то как я понимаю, мы не можем обеспечить такое-же поведение по умолчанию:
template <typename type_t>
type_t func()
{
    type_t a;   // Тут не происходит инициализации и в случае любого другого POD и в случае T
    type_t b(); // А вот тут в случае POD происходит инициализация нулями, а в случае T её нет ???
}

Как быть в таком случае и обеспечить однообразное обобщенное поведение ? По идее, в шаблонном методе можно написать что-то типа: type_t a(0), но тогда все типы должны будут иметь конструктор который принимает 0. Вопрос к знатокам, как правильно разрулить такую ситуацию?
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 05.09.2021 11:57 Videoman . Предыдущая версия .
Re: Zero initialization
От: rg45 СССР  
Дата: 05.09.21 18:20
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Теперь если смотреть на все это в контексте какого-нибудь шаблонно метода, то как я понимаю, мы не можем обеспечить такое-же поведение по умолчанию:

V>
V>    type_t a;   // Тут не происходит инициализации и в случае любого другого POD и в случае T
V>


Здесь происходит Default initialization в любом случае. Просто этот вид инициализации по-разному работает для POD и не-POD типов.

V>
V>    type_t b(); // А вот тут в случае POD происходит инициализация нулями, а в случае T её нет ???
V>


А вот тут ты попался. Не происходит здесь никакой инициализации, потому что это просто объявление функции b без параметров, с типом возвращаемого значения type_t: http://coliru.stacked-crooked.com/a/f697f7b4c41fa766

V>Как быть в таком случае и обеспечить однообразное обобщенное поведение ? По идее, в шаблонном методе можно написать что-то типа: type_t a(0), но тогда все типы должны будут иметь конструктор который принимает 0. Вопрос к знатокам, как правильно разрулить такую ситуацию?


Не понимаю я, что ты хочешь здесь разрулить. Инициализация объекта-то происходит (вызывается определенный пользователем дефолтный конструктор). Просто разработчик класса Т решил, что при инициализации объекта класса инкапсулируемые этим объектом члены-данные должны остаться без инициализации, такова его воля. Он с таким же успехом мог бы заполнить члены данные какими-то специальными значениями и снова было бы не так, как в POD. На то они и конструкторы, чтобы полностью контролировать процесс инициализации объекта. Вот если эти члены-данные являются открытыми (public) и остались без инициализации, то такому разработчику следовало бы надавать по рукам, конечно.
--
Отредактировано 05.09.2021 19:01 rg45 . Предыдущая версия . Еще …
Отредактировано 05.09.2021 18:45 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 18:40 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 18:30 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 18:29 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 18:22 rg45 . Предыдущая версия .
Re[2]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 05.09.21 20:00
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здесь происходит Default initialization в любом случае. Просто этот вид инициализации по-разному работает для POD и не-POD типов.

Согласен что по разному, поэтому и уточняю что я рассматриваю здесь только POD типы.
R>
R>    type_t b(); // А вот тут в случае POD происходит инициализация нулями, а в случае T её нет ???
R>

R>А вот тут ты попался. Не происходит здесь никакой инициализации, потому что это просто объявление функции b без параметров, с типом возвращаемого значения type_t: http://coliru.stacked-crooked.com/a/f697f7b4c41fa766

Ну ладно, ладно, подловил, думал прокатит . Давай зайдем с другой стороны и подправим тогда пример. Представим что где-то в шаблонном методе мы делаем следующее:
template <typename type_t>
void func1(const type&);
template <typename type_t>
void func2()
{
    func1(type_t()); // Передаем некий класс и явно вызываем конструктор по умолчанию
}
Вот тут вот например для int будет передан 0, а для T будет вызван дефолтный конструктор, который может либо инициализировать Т, либо нет, в зависимости от реализации.
Мой вопрос, как мне реализовать дефолтный конструктор T, что бы он:
T a; - Ничего не инициализировал
T a = T(); - Инициализировал

т.е. я хочу в своем классе T имитировать поведение настоящих POD типов, можно ли такое сделать?
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[3]: Zero initialization
От: rg45 СССР  
Дата: 05.09.21 20:17
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Мой вопрос, как мне реализовать дефолтный конструктор T, что бы он:

V>
V>T a; - Ничего не инициализировал
V>T a = T(); - Инициализировал 
V>

V>т.е. я хочу в своем классе T имитировать поведение настоящих POD типов, можно ли такое сделать?

Думаю, что нельзя. Потому что в обоих случаях будет вызван один и тот же реализованный тобой конструктор. А реализовать в одном конструкторе два разных поведения не получится. Ибо получить информацию о способе инициализации (default или value) внутри конструктора вряд ли возможно. Ну либо я буду очень удивлен
--
Отредактировано 05.09.2021 21:06 rg45 . Предыдущая версия . Еще …
Отредактировано 05.09.2021 21:06 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 20:44 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 20:23 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 20:22 rg45 . Предыдущая версия .
Отредактировано 05.09.2021 20:17 rg45 . Предыдущая версия .
Re[4]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 05.09.21 21:33
Оценка:
Здравствуйте, rg45, Вы писали:

R>Думаю, что нельзя. Потому что в обоих случаях будет вызван один и тот же реализованный тобой конструктор. А реализовать в одном конструкторе два разных поведения не получится. Ибо получить информацию о способе инициализации (default или value) внутри конструктора вряд ли возможно. Ну либо я буду очень удивлен


Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[5]: Zero initialization
От: rg45 СССР  
Дата: 06.09.21 06:18
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.


Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором. То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.
--
Re[3]: Zero initialization
От: flаt  
Дата: 06.09.21 15:02
Оценка:
Здравствуйте, Videoman, Вы писали:
]
V>Мой вопрос, как мне реализовать дефолтный конструктор T, что бы он:
V>
V>T a; - Ничего не инициализировал
V>T a = T(); - Инициализировал 
V>

V>т.е. я хочу в своем классе T имитировать поведение настоящих POD типов, можно ли такое сделать?


В лоб не получится, но можно подумать о тегах и вызывать разные конструкторы.
Re[6]: Zero initialization
От: kov_serg Россия  
Дата: 06.09.21 18:34
Оценка:
Здравствуйте, rg45, Вы писали:

V>>Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.


R>Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором. То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.


А кто мешает это контролировать вручную?
template<class T>T* newPOD() {
    char* data=new char[sizeof(T)];
    if (data) memset(data,0,sizeof(T));
    return (T*)data;
}

template<class T>struct POD {
    char data[sizeof(T)];
    POD() { memset(data,0,sizeof(T)); }
    T* operator ->() { return (T*)data; }
};

...
POD<Point> sp;
moveto(sp->x,sp->y);

Point *dp=newPOD<Point>();
moveto(dp->x,dp->y);
delete dp;

Или trait-ами?
Re[6]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 06.09.21 20:10
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором.


В данном случае меня не интересует совсем общий случай. Вопрос только про POD типы без переопределенного конструктора и мой, полностью подвластный мне тип T, где я точно знаю что такое "занулить".

R>То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.


Да нет же, в общем виде обходить у произвольного (не моего) класса дефолтный конструктор мне не нужно.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[4]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 06.09.21 20:25
Оценка:
Здравствуйте, flаt, Вы писали:

F>В лоб не получится, но можно подумать о тегах и вызывать разные конструкторы.


Жаль. В принципе я готов к такой реальности. Вопрос на самом деле возник лишь из-за удобства и надежности написания шаблонного кода. Просто не зная какой конкретно тип передается мне придется писать:
type_t a;    // Если я не хочу "занулять" экземпляр и,
type_t a(0); // Если я хочу "занулить" экземпляр. Что накладывает определенные ограничения на типы, чего хотелось бы избежать.


Весь сыр-бор разгорелся из-за кода в шаблонной библиотеке, где я по привычке вызываю type_t() для инициализации. Потом оказалось что в моем собственном классе T, который по сути тоже POD, конструктор по умолчанию, с инициализацией нулями, жрал слишком много CPU и инициализацию пришлось убрать. После этого шаблонный код type_t() стал работать то инициализируя, то нет, в зависимости от переданного типа. Пока всё это лишь вопрос удобства и надежности кода.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 06.09.2021 20:26 Videoman . Предыдущая версия . Еще …
Отредактировано 06.09.2021 20:25 Videoman . Предыдущая версия .
Re[7]: Zero initialization
От: rg45 СССР  
Дата: 07.09.21 05:28
Оценка:
Здравствуйте, Videoman, Вы писали:

V>В данном случае меня не интересует совсем общий случай. Вопрос только про POD типы без переопределенного конструктора и мой, полностью подвластный мне тип T, где я точно знаю что такое "занулить".


А как тебе идея использовать специальную обертку для создания неинциализированных объектов?

template <typename T>
class Uninitialized
{
public:

  Unititialized(){}  

  T& operator * () {return reinterpret_cast<T&>(m_storage); }
  const T& operator * () const {return reinterpret_cast<const T&>(m_storage); }
  T* operator -> () {return reinterpret_cast<T*>(m_storage); }
  const T* operator -> () const {return reinterpret_cast<const T*>(m_storage); }

private:
  char m_storage[sizeof(T)];
};

Unitialized<type_t> a;   // Тут не происходит инициализации и в случае любого другого POD и в случае T
type_t b{}; // А вот тут происходит, при нормально написанном конструкторе по умолчанию


UB, конечно — при использовании для non-POD типов. Зато наглядно
--
Отредактировано 07.09.2021 5:32 rg45 . Предыдущая версия .
Re[8]: Zero initialization
От: _NN_ www.nemerleweb.com
Дата: 07.09.21 05:55
Оценка: +1
Здравствуйте, rg45, Вы писали:

Небольшая поправка с выравниванием.

template <typename T>
class Uninitialized
{
public:

  Unititialized(){}  

  T& operator * () {return reinterpret_cast<T&>(m_storage); }
  const T& operator * () const {return reinterpret_cast<const T&>(m_storage); }
  T* operator -> () {return reinterpret_cast<T*>(m_storage); }
  const T* operator -> () const {return reinterpret_cast<const T*>(m_storage); }

private:
  alignas(T) char m_storage[sizeof(T)];
};
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: Zero initialization
От: _NN_ www.nemerleweb.com
Дата: 07.09.21 06:00
Оценка:
Здравствуйте, Videoman, Вы писали:

Может зайти с другой стороны ?
Конструктор по умолчанию без инициализации, и специальный для.

struct init_t {};
static constexpr init_t init;

struct A
{
  A() {} // нет инициализации
  A(init_t) : x(), y(), z() {}

  int x,y,z;
};

int main()
{
  A unknown_values;
  cout << unknown_values; // UB

  A zero_values(init);
  cout << zero_values.x; // 0
}
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[2]: Zero initialization
От: rg45 СССР  
Дата: 07.09.21 06:05
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Может зайти с другой стороны ?

_NN>Конструктор по умолчанию без инициализации, и специальный для.

Как я понял, для него важно добиться, чтобы один и тот же шаблонный код работал одинаково для POD и non-POD.
--
Re[3]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 07.09.21 08:49
Оценка:
R>Как я понял, для него важно добиться, чтобы один и тот же шаблонный код работал одинаково для POD и non-POD.

Давайте я тогда совсем конкретно опишу в чем проблема, что бы сузить количество вариантов, а-то слишком видимо абстрактно получилось:
У меня на досуге случайно получилась кроссплатформенная, header-only, библиотека для длинных целых которые в 2-4 раза больше чем максимально поддерживаемые тем или иным компилятором, типа 128-бит, 256-бит. Редко, но бывает нужно быстро что-то точно (в пределах целых) вычислить за пределами максимальной точности и потом вернуть все обратно в нативный диапазон. Я хочу ее выложить как open-source. Но в процессе eё написания я 2 раза наступал на грабли в месте инициализации длинного целого, т.к. по привычке писал uint128_t(), зная что значение "занулится". А вот мой класс uint128_t, по соображениям производительности, имеет дефолтный конструктор который не выполняет инициализации и я несколько раз получал мусор. Отсюда у меня появился вопрос: можно ли как-то полностью скопировать поведение "родного" int? Оказывается, что видимо нет. Да, все идеи с фабрикой и конструктором принимающим int — рабочие, но это все в моем коде. Сторонний код пользователя я не могу контролировать и такой подход не сработает.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[4]: Zero initialization
От: rg45 СССР  
Дата: 07.09.21 10:20
Оценка: 11 (2) +1
Здравствуйте, Videoman, Вы писали:

V>А вот мой класс uint128_t, по соображениям производительности, имеет дефолтный конструктор который не выполняет инициализации и я несколько раз получал мусор.


А тебе принципиально, чтобы этот конструктор был определен тобой? Автоматически сгенерированный компилятором дефолтный конструктор не подойдет тебе? В этом случае ты можешь иметь в классе набор каких-то кастомных конструторов, но при этом класс сохранит поведение при инициализации подобное POD-ам:

http://coliru.stacked-crooked.com/a/6e21f1674baee964

#include <iostream>

struct uint128_t
{
    int a;
    double b;
    const int* c;

    uint128_t() = default;

    uint128_t(int, double = {}, const int* = {}); // Какие-то еще другие конструкторы
};

void test(const uint128_t& v)
{
    std::cout << "{ " << v.a << ", " << v.b << ", " << v.c << " }" << std::endl;
}

int main()
{
    uint128_t x; // No initialization
    uint128_t y = uint128_t(); // Zero-initialization
    uint128_t z{}; // Zero-initialization

    test(x);
    test(y);
    test(z);
}
--
Re[5]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 07.09.21 11:20
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>А тебе принципиально, чтобы этот конструктор был определен тобой?

Нет конечно. А ты гений!!! Об этом я что-то совсем не подумал, хотя часто использую default. Просто не туда цепочки рассуждений ушли все — Работает, как нативный тип!!!
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Отредактировано 07.09.2021 11:21 Videoman . Предыдущая версия .
Re[6]: Zero initialization
От: rg45 СССР  
Дата: 08.09.21 11:36
Оценка:
Здравствуйте, Videoman, Вы писали:

V>Работает, как нативный тип!!!


Тут вот какая интересная деталь. Мы можем в класс из этого примера добавить какой-нибудь член non-POD типа, например, std::string. Это приведет к тому, что весь класс станет non-POD типом. Но даже после этого поведение default/value intitialization останется таким же как прежде: http://coliru.stacked-crooked.com/a/cb6c2173e51a420b. И конструктор с таким поведением может предоставить только сам компилятор, но не программист. Какая-то тут недоработка в требованиях к языку, по-моему.
--
Отредактировано 08.09.2021 11:41 rg45 . Предыдущая версия . Еще …
Отредактировано 08.09.2021 11:41 rg45 . Предыдущая версия .
Отредактировано 08.09.2021 11:40 rg45 . Предыдущая версия .
Отредактировано 08.09.2021 11:37 rg45 . Предыдущая версия .
Re[7]: Zero initialization
От: Videoman Россия https://hts.tv/
Дата: 08.09.21 12:39
Оценка:
Здравствуйте, rg45, Вы писали:

R>Тут вот какая интересная деталь. Мы можем в класс из этого примера добавить какой-нибудь член non-POD типа, например, std::string. Это приведет к тому, что весь класс станет non-POD типом. Но даже после этого поведение default/value intitialization останется таким же как прежде: http://coliru.stacked-crooked.com/a/cb6c2173e51a420b. И конструктор с таким поведением может предоставить только сам компилятор, но не программист. Какая-то тут недоработка в требованиях к языку, по-моему.


Класс станет не POD, но std::string -то нормально инициализируется. Т.е. всё зависит от самим членов структуры, так что, я считаю, это нормальное поведение.
http://www.gravatar.com/avatar/60560936caa07b944d4c3cecf1c06cc5?s=80&d=identicon
Re[4]: Zero initialization
От: imh0  
Дата: 08.09.21 12:51
Оценка:
Здравствуйте, Videoman, Вы писали:

V>У меня на досуге случайно получилась кроссплатформенная, header-only, библиотека для длинных целых которые в 2-4 раза больше чем максимально поддерживаемые тем или иным компилятором, типа 128-бит, 256-бит.


https://gmplib.org/

==
По теме вопроса — посмотрите, это действительно важно — будет ли у вас работать такое...

map<int,{ваш тип}> mmm;

mmm[234]++;

При условии, что по умолчанию, предположим, в вашем типе, поумолчанию ноль.
Отредактировано 08.09.2021 12:58 imh0 . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.