Возьмем какой-нибудь 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. Вопрос к знатокам, как правильно разрулить такую ситуацию?
Здравствуйте, 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) и остались без инициализации, то такому разработчику следовало бы надавать по рукам, конечно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, 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 типов, можно ли такое сделать?
Здравствуйте, Videoman, Вы писали:
V>Мой вопрос, как мне реализовать дефолтный конструктор T, что бы он: V>
V>T a; - Ничего не инициализировал
V>T a = T(); - Инициализировал
V>
V>т.е. я хочу в своем классе T имитировать поведение настоящих POD типов, можно ли такое сделать?
Думаю, что нельзя. Потому что в обоих случаях будет вызван один и тот же реализованный тобой конструктор. А реализовать в одном конструкторе два разных поведения не получится. Ибо получить информацию о способе инициализации (default или value) внутри конструктора вряд ли возможно. Ну либо я буду очень удивлен
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Думаю, что нельзя. Потому что в обоих случаях будет вызван один и тот же реализованный тобой конструктор. А реализовать в одном конструкторе два разных поведения не получится. Ибо получить информацию о способе инициализации (default или value) внутри конструктора вряд ли возможно. Ну либо я буду очень удивлен
Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.
Здравствуйте, Videoman, Вы писали:
V>Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.
Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором. То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
V>>Вот и хотелось бы поставить точку в этом вопросе. Абстракция протекает и не получается обеспечить сходное поведение. В случае, если обнуляющий конструктор слишком дорог по тем или иным причинам, получается нет способа явно занулить объект в шаблонном коде в зависимости POD это или пользовательский класс.
R>Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором. То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.
Здравствуйте, rg45, Вы писали:
R>Ну а что значит "занулить" в общем случае? В общем же случае в классе могут быть такие члены-данные, которые не предусматривают конструктора по умолчанию. Ссылки, например, которые каким-то образом инициализируются дефолтным конструктором.
В данном случае меня не интересует совсем общий случай. Вопрос только про POD типы без переопределенного конструктора и мой, полностью подвластный мне тип T, где я точно знаю что такое "занулить".
R>То есть, ты вопрос ставишь так: можно ли предоставить для класса конструктор по умолчанию, а потом его обойти при инициализации? Думаю, ценность конструкторов сильно упала бы, если бы это было возможно.
Да нет же, в общем виде обходить у произвольного (не моего) класса дефолтный конструктор мне не нужно.
Здравствуйте, flаt, Вы писали:
F>В лоб не получится, но можно подумать о тегах и вызывать разные конструкторы.
Жаль. В принципе я готов к такой реальности. Вопрос на самом деле возник лишь из-за удобства и надежности написания шаблонного кода. Просто не зная какой конкретно тип передается мне придется писать:
type_t a; // Если я не хочу "занулять" экземпляр и,
type_t a(0); // Если я хочу "занулить" экземпляр. Что накладывает определенные ограничения на типы, чего хотелось бы избежать.
Весь сыр-бор разгорелся из-за кода в шаблонной библиотеке, где я по привычке вызываю type_t() для инициализации. Потом оказалось что в моем собственном классе T, который по сути тоже POD, конструктор по умолчанию, с инициализацией нулями, жрал слишком много CPU и инициализацию пришлось убрать. После этого шаблонный код type_t() стал работать то инициализируя, то нет, в зависимости от переданного типа. Пока всё это лишь вопрос удобства и надежности кода.
Здравствуйте, 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 типов. Зато наглядно
--
Не можешь достичь желаемого — пожелай достигнутого.
R>Как я понял, для него важно добиться, чтобы один и тот же шаблонный код работал одинаково для POD и non-POD.
Давайте я тогда совсем конкретно опишу в чем проблема, что бы сузить количество вариантов, а-то слишком видимо абстрактно получилось:
У меня на досуге случайно получилась кроссплатформенная, header-only, библиотека для длинных целых которые в 2-4 раза больше чем максимально поддерживаемые тем или иным компилятором, типа 128-бит, 256-бит. Редко, но бывает нужно быстро что-то точно (в пределах целых) вычислить за пределами максимальной точности и потом вернуть все обратно в нативный диапазон. Я хочу ее выложить как open-source. Но в процессе eё написания я 2 раза наступал на грабли в месте инициализации длинного целого, т.к. по привычке писал uint128_t(), зная что значение "занулится". А вот мой класс uint128_t, по соображениям производительности, имеет дефолтный конструктор который не выполняет инициализации и я несколько раз получал мусор. Отсюда у меня появился вопрос: можно ли как-то полностью скопировать поведение "родного" int? Оказывается, что видимо нет. Да, все идеи с фабрикой и конструктором принимающим int — рабочие, но это все в моем коде. Сторонний код пользователя я не могу контролировать и такой подход не сработает.
Здравствуйте, Videoman, Вы писали:
V>А вот мой класс uint128_t, по соображениям производительности, имеет дефолтный конструктор который не выполняет инициализации и я несколько раз получал мусор.
А тебе принципиально, чтобы этот конструктор был определен тобой? Автоматически сгенерированный компилятором дефолтный конструктор не подойдет тебе? В этом случае ты можешь иметь в классе набор каких-то кастомных конструторов, но при этом класс сохранит поведение при инициализации подобное POD-ам:
Здравствуйте, rg45, Вы писали:
R>А тебе принципиально, чтобы этот конструктор был определен тобой?
Нет конечно. А ты гений!!! Об этом я что-то совсем не подумал, хотя часто использую default. Просто не туда цепочки рассуждений ушли все — Работает, как нативный тип!!!
Здравствуйте, Videoman, Вы писали:
V>Работает, как нативный тип!!!
Тут вот какая интересная деталь. Мы можем в класс из этого примера добавить какой-нибудь член non-POD типа, например, std::string. Это приведет к тому, что весь класс станет non-POD типом. Но даже после этого поведение default/value intitialization останется таким же как прежде: http://coliru.stacked-crooked.com/a/cb6c2173e51a420b. И конструктор с таким поведением может предоставить только сам компилятор, но не программист. Какая-то тут недоработка в требованиях к языку, по-моему.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Тут вот какая интересная деталь. Мы можем в класс из этого примера добавить какой-нибудь член non-POD типа, например, std::string. Это приведет к тому, что весь класс станет non-POD типом. Но даже после этого поведение default/value intitialization останется таким же как прежде: http://coliru.stacked-crooked.com/a/cb6c2173e51a420b. И конструктор с таким поведением может предоставить только сам компилятор, но не программист. Какая-то тут недоработка в требованиях к языку, по-моему.
Класс станет не POD, но std::string -то нормально инициализируется. Т.е. всё зависит от самим членов структуры, так что, я считаю, это нормальное поведение.
Здравствуйте, Videoman, Вы писали:
V>У меня на досуге случайно получилась кроссплатформенная, header-only, библиотека для длинных целых которые в 2-4 раза больше чем максимально поддерживаемые тем или иным компилятором, типа 128-бит, 256-бит.