Форум программистов, компьютерный форум, киберфорум
C++
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,114
Записей в блоге: 2

Создать объект минуя конструктор

13.05.2026, 08:41. Показов 3683. Ответов 40
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день

Опять долго нет никаких тем, даже глупых. Хорошо, вот слышал что можно создать объект минуя вызов его конструктора, хотя таковой имеется. Как-то через темплейт. Правда или нет - не знаю. Зачем нужно такое извращение - лично мне незачем, чисто спортивный интерес. На всякий случай конкретизируем вопрос
C++
1
2
3
4
5
struct MyClass {
 MyClass( int data ) : m_data(data) {}
 
 int m_data;
};
Конечно речь не идет о примитивных приведениях
C++
1
MyClass * mc = (MyClass *) malloc(sizeof(MyClass));
Это всем известно и никому не интересно. Спрашивается можно ли как-то легально создать MyClass так чтобы конструктор не был вызван?

Спасибо
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
13.05.2026, 08:41
Ответы с готовыми решениями:

Создать класс "Вектор" и реализовать конструктор по умолчанию, конструктор копирования и деструктор
Всем доброго времени суток! нужна ваша помощь! нужно создать класс вектор и реализовать...

Создать класс. Написать конструктор по умолчанию, конструктор с параметрами. Перегрузить операции «меньше» и «равно»
Помогите пожалуйста, хоть с одним из заданий. 1. Создать класс. Написать конструктор по...

Для класса создать конструктор инициализации, конструктор копирования и преобразования типов, set
Для класса создать конструктор инициализации, конструктор копирования и преобразования типов, set-,...

40
 Аватар для vlisp
1070 / 991 / 153
Регистрация: 10.08.2015
Сообщений: 5,413
16.05.2026, 10:46
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от Igor3D Посмотреть сообщение
Опять долго нет никаких тем, даже глупых.
и ты решил создать самую глупую?
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6312 / 3035 / 1053
Регистрация: 01.06.2021
Сообщений: 11,552
16.05.2026, 11:53
Цитата Сообщение от Igor3D Посмотреть сообщение
Опять долго нет никаких тем, даже глупых.
Как понимаю, у тебя просто много свободного времени. Почему бы не создать какое-нибудь приложение или игру? Зачем все эти теоретические темы?
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
16.05.2026, 21:13
Цитата Сообщение от jcxz Посмотреть сообщение
Вот это и делает конструктор. Значит - это одно и то же.
Нет, не значит, потому что конструктор делает не только это

Конструктор - специальная функция-член класса, которая может:
1. Запускать конструктор базового класса.
2. Делегировать вызов другому конструктору.
3. Исполнять список инициализации.
4. Исполнять кастомную логику, описанную в теле конструктора.

А ваше это - это только лишь 3 пункт.
И кстати, конструктор не обязан его исполнять.
Выше вам привели пример ситуации, когда данные-члены остались неинициализированными.

Цитата Сообщение от jcxz Посмотреть сообщение
Если функция ничего не делает, то значит её нет.
Странная у вас логика. Из того факта, что Вася — бездельник, никак не следует, что Васи не существует.

Цитата Сообщение от jcxz Посмотреть сообщение
Оптимизирующий компилятор
Не принципиальный фактор.
Значение имеют правила языка с++, и алгоритм, который описал программист на этом языке.
Например, программист может описать вызов ничего не делающей функции, а компилятор может оптимизировать этот вызов.
Однако, оптимизация никак не отменяет факт наличия вызова функции в алгоритме, который описал программист.
Формально (с точки зрения языка) в исходном коде описан вызов функции.

Для примера рассмотрим такую ситуацию: программист разрабатывает статическую библиотеку.
Он описал в глобальной области файла.cpp ничего не делающую функцию:
C++
1
void foo(){}
Несмотря на то, что функция ничего не делает: не создает никаких сайд-эффектов, тем не менее, ни компилятор, ни линкер, не могут выкинуть эту функцию, невзирая ни на какие оптимизации.

Объясняется это очень просто: функция определена. То есть, объективно она существует, и имеет внешнее связывание. В общем случае ни компилятор, ни линкер не могут предсказать, кто и как может вызвать такую функцию. Например, кто-то может слинковаться с библиотекой, срисовать адрес функции, и сделать вызов по указателю. А компилятор — не телепат, он понятия не имеет, что именно там может быть вызвано по указателю: пустышка, или не пустышка, поэтому ему ничего не остается, кроме как сгенерировать код для "честного" вызова функции. И если это окажется вызов ничего не делающей функции, значит это будет честный вызов ничего не делающей функции.

Цитата Сообщение от jcxz Посмотреть сообщение
делать в конструкторе что-то иное, кроме инициализации объекта - крайне плохой стиль программирования.
Ваше оценочное суждение никак не отменяют тот факт, что в конструкторе можно делать много чего ещё, помимо инициализации.
Суть в том, что ставить знак равенства между конструктором и инициализаций — некорректно.
Инициализация — это лишь часть того, что можно делать в конструкторе.

Цитата Сообщение от jcxz Посмотреть сообщение
назначение конструктора == инициализация объекта
Формально, инициализация экземпляра класса заканчивается в момент завершения работы списков инициализации, однако работа конструктора на этом ещё не заканчивается.

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

Например, нужно залоггировать значения полей


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <string>
 
struct descript
{
    descript(std::string n, std::string g) noexcept
        : name(std::move(n))
        , group(std::move(g))            
    {
        std::cout << "descript(" 
            << this->name 
            << ", " 
            << this->group
            << "): was built\n";
    }
    
    std::string name;
    std::string group;
};
 
int main()
{
    descript { "Max", "develop" };
}


Цитата Сообщение от jcxz Посмотреть сообщение
Здесь конструктор - встроенный в компилятор
Это — бредовая формулировка.
В компилятор можно встроить, например, телеметрию какую-нибудь.
Или поддержку каких-то дополнительных инструментов.

Конструкторы — это часть языка, а не компилятора.

У фундаментальных типов конструкторов не существует.
Однако, существует синтаксический сахар, который весьма похож на вызов конструктора.

Например, конструкции вида:

C++
1
2
3
// В шаблоне
t v1 = t{};
t v2 = t();
Более полный пример


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <cassert>
 
template<typename t> void foo(int v)
{
    t v1 = t{};    // initialize with zero
    t v2{};        // initialize with zero
    t v3{ v };     // initialize with 1
    
    std::cout << "v1: " << v1 << '\n';
    std::cout << "v2: " << v2 << '\n';
    std::cout << "v3: " << v3 << '\n';    
    assert(v1 == v2);    
}
 
struct bar
{
    bar(int const v = 0) noexcept
        : val(v)
    {}
    
    operator int() const noexcept 
        { return this->val; }
    
    int val;
};
 
int main()
{
    foo<int>(1);
    foo<bar>(1);    
}


Для классов компилятор действительно сгенерирует вызовы конструкторов.
А для фундаментальных типов задействует "фиктивные конструкторы", которые и внешне, и по поведению похожи на настоящие.

Словосочетание "фиктивные конструкторы" в кавычках, потому что это неофициальный термин.
В различных источниках ещё можно встретить обозначение "псевдо-конструкторы", но это тоже неофициальный термин.
Так же можно встретить понятие: "псевдо-конструкторы реализуются через Functional cast"
И, что характерно, словосочетание Functional cast можно встретить даже в тексте стандарта, но от этого оно официальным термином не становится.

Официально жеж

Фиктивные конструкторы реализуются через явное преобразование типов в функциональной нотации, которая называется Explicit type conversion (functional notation)
Учитывая, что словосочетание functional cast notation упоминается с явной ссылкой на Explicit type conversion (functional notation), то вполне корректно полагать их синонимами.

В свою очередь, правила Explicit type conversion (functional notation) ссылаются на общие правила инициализации

1. Для конструкций вида: t v1 = t{}; или t v1 = t(); — это Functional cast, который запускает Direct-initialization, который в свою очередь запускает Value-initialization

2. Для конструкции вида: t v2{}; или t v3{v}; — запуск List-initialization
Где:
- Для пустых скобок t v2{}; отрабатывает Value-initialization,
- А для t v3{v}; отрабатывает Direct-list-initialization.

3. Для конструкции вида: t v3(v); - это Direct-initialization

4. Для конструкции вида: t v4; - это Default-initialization, которая для фундаментальных типов ничего не делает, поэтому значение такого объекта будет indeterminate value, чтение которого - Undefined Behavior

Про четвертый пункт см. ниже.



Несмотря на то, что фиктивные конструкторы весьма похожи на настоящие, у них есть важное отличие, которое нужно иметь ввиду.

Потому что иначе можно налететь на UB


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <cassert>
 
template<typename t> void foo(int v)
{
    t v1 = t{};
    t v2{};
    t v3{ v };    
    
    volatile t v4;
    const t& v5 = const_cast<const t&>(v4);
   
    std::cout << "v1: " << v1 << '\n';
    std::cout << "v2: " << v2 << '\n';
    std::cout << "v3: " << v3 << '\n';    
    std::cout << "v5: " << v5 << '\n'; // <--- UB
    assert(v1 == v2);
    assert(v1 == v5); // Abort signal from abort(3) (SIGABRT)
}
 
struct bar
{
    bar(int const v = 0) noexcept
        : val(v)
    {}
   
    operator int() const noexcept 
        { return this->val; }
    
    int val;
};
 
void dirty_stack() 
{
    volatile int array[1000]; 
    for(int i = 0; i < 1000; ++i)
        array[i] = 12345;
    (void) array;
}
 
int main()
{
    dirty_stack();
    foo<int>(1);
    foo<bar>(1);        
}



Итого:
1. Строго говоря, конструктор может оставить экземпляр без инициализации.
А так же может делать вещи, которые не связаны напрямую с инициализацией.
Поэтому ставить знак равенства между конструктором и инициализацией принципиально некорректно.

2. У фундаментальных типов вообще нет конструкторов.

3. Можно считать, что у фундаментальных типов есть фиктивные конструкторы, но при этом важно понимать, что на самом деле представляют собой такие "фиктивные конструкторы", и каково принципиальное отличие от настоящих.
3
332 / 126 / 8
Регистрация: 19.07.2024
Сообщений: 663
16.05.2026, 21:24
Цитата Сообщение от eva2326 Посмотреть сообщение
Нет, не значит, потому что конструктор делает не только это
Конструктор - специальная функция-член класса, которая может:
1. Запускать конструктор базового класса.
2. Делегировать вызов другому конструктору.
3. Исполнять список инициализации.
4. Исполнять кастомную логику, описанную в теле конструктора.
А ваше это - это только лишь 3 пункт.
И кстати, конструктор не обязан его исполнять.
А остальные пункты - обязан что-ли???
Если всё-таки не обязан и не исполняет ничего кроме 3-го пункта - он остаётся конструктором или нет?
Если да, то тогда вы сами себе противоречите. Не замечаете?

Вобщем - у вас явные проблемы с логикой.

Цитата Сообщение от eva2326 Посмотреть сообщение
Например, программист может описать вызов ничего не делающей функции, а компилятор может оптимизировать этот вызов.
Однако, оптимизация никак не отменяет факт наличия вызова функции в алгоритме, который описал программист.
Наличие того, чего нет??? Раз компилятор оптимизировал (удалил) вызов пустой функции, значит этого вызова нет.

Цитата Сообщение от eva2326 Посмотреть сообщение
Он описал в глобальной области файла.cpp ничего не делающую функцию:
C++Скопировано
1
void foo(){}
Несмотря на то, что функция ничего не делает: не создает никаких сайд-эффектов, тем не менее, ни компилятор, ни линкер, не могут выкинуть эту функцию, невзирая ни на какие оптимизации.
И могут и выкидывают.
Более того - компиляторы даже и могут и выкидывают не только пустые функции, но и не пустые, если результат их работы нигде не используется.
Более того - это одна из главных задач компоновщика. Иначе любая программа включала бы все возможные функции, описанные в подключаемых библиотеках, и весила бы побольше полной винды.

Цитата Сообщение от eva2326 Посмотреть сообщение
Это — бредовая формулировка.
Бред - это всё что вы понаписали.


PS: Как видно - вы взялись рассуждать о том, о чём понятия не имеете. Да ещё и с логикой явные проблемы.
Займитесь реально программированием, а не теоретизированиями с дивана, и узнаете и это и многое другое, новое и удивительное.
0
Эксперт функциональных языков программированияЭксперт С++
 Аватар для Royal_X
6312 / 3035 / 1053
Регистрация: 01.06.2021
Сообщений: 11,552
16.05.2026, 22:01
Цитата Сообщение от jcxz
Более того - компиляторы даже и могут и выкидывают не только пустые функции, но и не пустые, если результат их работы нигде не используется.
Верно. Я когда-то экспериментировал с этим в C++: написал функцию, которая нигде не вызывается, скомпилировал с оптимизацией и сравнил ассемблерный вывод - с функцией и без неё. Выводы оказались идентичны.
Это поведение называется Dead Code Elimination (DCE) - одна из стандартных оптимизаций, которую выполняет компилятор. GCC и Clang убирают неиспользуемые функции уже на уровне -O1. Причём если функция объявлена в единице трансляции без __attribute__((used)) или аналогов, компилятор вправе выбросить её полностью.
Стоит оговориться: речь идёт о функциях с внутренней компоновкой (static) или шаблонах. Функции с внешней компоновкой (без static, в .cpp-файле) компилятор обычно сохраняет - их может вызвать другая единица трансляции. Но линковщик с включённым LTO (Link-Time Optimization) или флагом -ffunction-sections + --gc-sections уберёт и их, если они не используются нигде в программе.
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
17.05.2026, 01:38
Цитата Сообщение от jcxz Посмотреть сообщение
А остальные пункты - обязан что-ли???
Странная эмоциональная реакция.
Особенно учитывая, что я не давала вам повода для такого вопроса.
Я нигде не писала, будто бы конструктор обязан выполнять 1, 2 и 4 пункты.

Я использовала глагол:
Цитата Сообщение от eva2326 Посмотреть сообщение
может

Цитата Сообщение от jcxz Посмотреть сообщение
Если да, то тогда вы сами себе противоречите. Не замечаете?
В варианте ответа "Да" нет никакого противоречия.
Вы бредите.
Проверить очень просто: вы не сможете внятно обосновать, в чем именно заключается противоречие.

Более того, сама постановка вашего вопроса:
Цитата Сообщение от jcxz Посмотреть сообщение
остаётся конструктором или нет?
Это - бред, потому что такая форма вопроса допускает мысль, что может и не остаться конструктором.
Однако, в с++ не существует условий при которых решается, останется ли конструктор конструктором, или не останется.
Если конструктор — конструктор, то он — уже конструктор, и конструктором быть не перестанет.

Цитата Сообщение от jcxz Посмотреть сообщение
Вобщем - у вас явные проблемы с логикой.
Голословное балабольство не очень интересно.
Интересны факты.
Желательно, с примерами кода, и со ссылками на черновик стандарта с++.

Цитата Сообщение от jcxz Посмотреть сообщение
Раз компилятор оптимизировал (удалил) вызов пустой функции, значит этого вызова нет.
Вы невнимательны.
Процитирую для вас ещё раз:

Цитата Сообщение от eva2326 Посмотреть сообщение
оптимизация никак не отменяет факт наличия вызова функции в алгоритме, который описал программист.
Здесь речь идет об алгоритме, который был написан программистом, а не о коде, который в итоге сгенерировал компилятор.

Цитата Сообщение от jcxz Посмотреть сообщение
И могут и выкидывают.
Нет, не могут.
Тут одно из двух: либо вы - балабол, либо очередная невнимательность с вашей стороны.

Процитирую на всякий случай сабж ещё раз:

Цитата Сообщение от eva2326 Посмотреть сообщение
программист разрабатывает статическую библиотеку.
Он описал в глобальной области файла.cpp ничего не делающую функцию:
void foo(){}
Вы точно знаете, что такое статическая библиотека?

Из статической библиотеки в принципе нельзя выкидывать функции с внешним связыванием.
Я ведь не просто так явным образом подчеркнула:
Цитата Сообщение от eva2326 Посмотреть сообщение
имеет внешнее связывание
Теоретически, любая функция с внешним связыванием может быть вызвана из клиентского кода.

Далее происходит следующее:

Цитата Сообщение от eva2326 Посмотреть сообщение
Например, кто-то может слинковаться с библиотекой, срисовать адрес функции, и сделать вызов по указателю.
Приведу код для наглядности:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Клиент, который линкуется со статической библиотекой
 
int rand_value(int const min, int const max);
 
void foo(); // Определена в статической библиотеке
void baz(); // Местная
 
void bar()
{
    using pfn = void(*)();
    pfn func = nullptr;
    if(rand_value(0, 1))
        func = foo;
    else
        func = baz;
    
    func();
}
И далее по тексту:

Цитата Сообщение от eva2326 Посмотреть сообщение
А компилятор — не телепат, он понятия не имеет, что именно там может быть вызвано по указателю: пустышка, или не пустышка, поэтому ему ничего не остается, кроме как сгенерировать код для "честного" вызова функции.
В таких обстоятельствах у компилятора просто нет выбора.
Ему придется сгенерировать честный вызов функции по указателю, а функция, соответственно, должна быть.
Иначе код сломается.


Цитата Сообщение от jcxz Посмотреть сообщение
выкидывают не только пустые функции, но и не пустые, если результат их работы нигде не используется.
Компилятор может выкинуть только неиспользуемую функцию c внутренним связыванием.

Линкер может выкинуть неиспользуемую функцию с внешним связыванием, но только если точно знает, что она не будет использована. Например, при сборке запускаемого (exe) приложения.
Однако к статическим библиотекам это не относится, потому что линкер — не телепат, и не может знать заранее, какие именно функции статической библиотеки будут востребованы её клиентами.

Так то, конечно, линкер может вырезать и из статической библиотеки тоже, но не сам, а только если его об этом явно попросить.

Например, для gcc как то так

С помощью комбинации флага --gc-sections и частичной линковки -Wl,-r можно попросить линкер «вырезать» неиспользуемые функции до того, как код превратится в финальный архив библиотеки.

Шаг 1: Сначала выполняем компиляцию в режиме "каждая функция в своей секции"
Bash
1
g++ -ffunction-sections -c foo.cpp bar.cpp
Шаг 2: Частичная линковка.
Вместо того чтобы отдавать файлы .o архиватору, нужно вызвать линкер с флагами частичной линковки, очистки, и явным указанием "корня".
Bash
1
g++ -Wl,-r -Wl,--gc-sections -Wl,-e,API_entry foo.o bar.o -o combined.o
Шаг 3: Создание библиотеки. Нужно упаковать получившийся очищенный файл combined.o в архив:
Bash
1
ar rcs libmylib.a combined.o
Критически важный нюанс: линкеру нужен корень (Root)

Если просто запустить: -Wl,-r -Wl,--gc-sections, тогда линкер выдаст ошибку:
Code
1
gc-sections requires either an entry or an undefined symbol
Как я уже писала выше, линкер — не телепат.
Ему нужно объяснить, что тут нужное, а что можно выкинуть.
Для этого ему нужно указать корень графа вызовов.
В обычном приложении таким корнем является функция main.
Но в статическое библиотеке нужно указывать явно одним из возможных способов:
1. Флаг точки входа (задаёт один главный корень): -Wl,-e, my_public_api_function
2. Флаг принудительного удержания: -Wl,--undefined=my_public_api_function
3. Специальный файл (линкер-скрипт), где перечислены все публичные функции.
(это самый частый подход для больших библиотек).

Итого:
Если указать линкеру, какие функции статической библиотеки являются публичными, тогда при запуске
-Wl,-r -Wl,--gc-sections он построит от них граф зависимостей, и вырежет всё лишнее, что не используется внутри самой библиотеки, и на выходе выдаст чистый .o файл для упаковки в .a.

Но автоматически, без подсказок со стороны программистов он такое делать не будет.
Ни LTO, ни отдельно взятые флаги -ffunction-sections, --gc-sections не заставят линкер вырезать функцию, в ситуации, когда он не знает, а вдруг она кому то нужна.


Цитата Сообщение от jcxz Посмотреть сообщение
Бред - это всё что вы понаписали.
Свои слова я подкрепила кодом и ссылками на черновик стандарта с++
А у вас только голословное балабольство без какой либо конкретики.
1
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
18.05.2026, 03:15
Цитата Сообщение от Igor3D Посмотреть сообщение
можно ли как-то легально создать MyClass так чтобы конструктор не был вызван?
Да. Представленный в вашем примере класс является Trivially Copyable
Значит можно использовать std::bit_cast

Кроме того, он является Implicit-lifetime type
Значит можно использовать std::malloc, и std::start_lifetime_as

Цитата Сообщение от Алексей1153 Посмотреть сообщение
создать объект типа MyClass без вызова конструктора не получится
Конечно, получится.

В с++ "создать объект" — значит запустить время его жизни.
Запускать время жизни объектов могут не только конструкторы, но и специально уполномоченные стандартные функции.
Например:
- std::malloc, std::calloc, std::realloc, std::memcpy, std::memmove,
- std::bit_cast, allocate у стандартных аллокаторов,
- std::start_lifetime_as, std::start_lifetime_as_array

Может быть ещё какие то имеются, или появятся.
Суть в том, что если стандарт наделяет некоторую функцию полномочиями создавать объекты, то он явным образом это регламентирует.
При этом стандарт четко регламентирует ограничения.
Например, для std::malloc ограничение: объекты должны быть Implicit-lifetime type

Детали


1. c.malloc неявно создает объекты по правилам intro.object,
2. Которые в свою очередь ссылаются на basic.types.general
3. Которые в свою очередь ссылаются на class.prop
4. Которые уже непосредственно определяют, какие классы обладают свойством implicit-lifetime

A class S is an implicit-lifetime class if
- it is an aggregate whose destructor is not user-provided or
- it has at least one trivial eligible constructor and a trivial, non-deleted destructor.
Итого: если некоторый класс удовлетворяет любому из обозначенных выше двух критериев, то std::malloc уполномочен запускать время жизни объектов такого класса.



Примечание: выражение new (и new[]) тоже относят к уполномоченным создавать объекты.
Но в действительности new лишь инициируют процедуру создания, а дальше работает конструктор.

Цитата Сообщение от Annemesski Посмотреть сообщение
все подобные финты типа
C++
1
2
3
4
5
6
7
8
struct Foo 
{
    int a;
    int b;
Foo(void) = delete;
};
...
Foo *obj = reinterpret_cast<Foo *>(std::malloc(sizeof (Foo)));
по стандарту UB
Никакое это не UB.

Уже 6 лет прошло с выхода с++20, в котором был принят Defect Report группы Ричарда Смита (P0593R6)
Статус Defect Report означает, что комитет признал старые правила ошибочными.

См. пункт 4

4. Accept as a Defect Report and apply the changes in P0593R6 (Implicit cre
ation of objects for low-level object manipulation) to the C++ working paper.


Собственно, именно по итогам P0593R6, в с++20 добавили Implicit-lifetime types, которое ознаменовало собой завершение вопиюще ханжеского безобразия: с++ наконец то признал сишное подмножество легальным.
Понадобилось всего каких то 22 года.
5
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
18.05.2026, 09:41
Цитата Сообщение от eva2326 Посмотреть сообщение
Уже 6 лет прошло с выхода с++20
Принято. Буду иметь в виду когда по работе понадобятся стандарты выше 17-го... лет через надцать.
Цитата Сообщение от Igor3D Посмотреть сообщение
Прыгать на адрес приведением некорректно для классов с виртуозами
Хранилище определено в рамках одного типа не предоставляющего интерфейсов для реинтепретации. Тут другая проблема, если пытаться этот контейнер для хранения полиморфов применять, то получишь срезку.
0
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,114
Записей в блоге: 2
18.05.2026, 17:17  [ТС]
Цитата Сообщение от eva2326 Посмотреть сообщение
можно ли как-то легально создать MyClass так чтобы конструктор не был вызван?
Да. Представленный в вашем примере класс является Trivially Copyable
Хмм... А как же (явно нетривиальный) конструктор MyClass? Вообще-то вопрос был о другом: можно ли как-то легально объявить/завести "неконструкторный" MyClass БЕЗ спрыгивая на адрес, т.е. приведения? Вроде бы нет, никак - но это недоказуемо. Разговор пошел в сторону "как/чем спрыгнуть" - не совсем то, но тоже неплохо. Только давайте "соединим теорию с практикой", а то чуть что - сразу стандартом (по голове ). Вот чисто практическая задачка

Есть ф-ция вызываемая сотни (и больше) раз
C++
1
SaveData2Undo( Stream & stream, TAG_ID, &MyClass );
Которую хотелось бы реализовать так
C++
1
2
3
4
5
6
template <class T>
void SaveData2Undo( Stream & stream, UInt32 id, const T * data )
{
 stream.write(id, sizeof(id));
 stream.write(data, sizeof(T));
}
И, по меньшей мере в 90-95% случаев, это верно, достаточно просто/тупо слить в поток простую структуру. Но есть злополучные 5-10% где это не катит. Как мне решить это на этапе компиляции? Т.е. если попробую написать такой вызов для неподходящего типа - получу что-то типа static_assert

Не по теме:

Цитата Сообщение от Igor3D Посмотреть сообщение
некорректно для классов с виртуозами
Писал "с виртуалами", недоглядел за (шустрой) автозаменой:)

0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
19.05.2026, 10:36
Цитата Сообщение от Igor3D Посмотреть сообщение
Как мне решить это на этапе компиляции?
добавить проверку типа, на вскидку
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <class T>
void SaveData2Undo( Stream & stream, UInt32 id, const T * data )
{
    static_assert(std::is_trivially_copyable<T>::value, "Template type is not valid must be a trivial copyable");
 
    stream.write(id, sizeof(id));
    stream.write(data, sizeof(T));
}
 
template <class T>
    requires std::is_trivially_copyable<T>::value
void SaveData2Undo( Stream & stream, UInt32 id, const T * data ) { ... }
 
template<class T, std::enable_if<std::is_trivially_copyable<T>::value, int>::type = 0>
void SaveData2Undo( Stream & stream, UInt32 id, const T * data ) { ... }
0
332 / 126 / 8
Регистрация: 19.07.2024
Сообщений: 663
20.05.2026, 11:30
Цитата Сообщение от eva2326 Посмотреть сообщение
Вы бредите.
Мадам, может не будем хамить, а???
Хамите своему мужу на кухне.
Цитата Сообщение от eva2326 Посмотреть сообщение
Проверить очень просто: вы не сможете внятно обосновать, в чем именно заключается противоречие.
Напоминаю, вы писали:
Цитата Сообщение от eva2326 Посмотреть сообщение
Конструктор - специальная функция-член класса, которая может:
1. Запускать конструктор базового класса.
2. Делегировать вызов другому конструктору.
3. Исполнять список инициализации.
4. Исполнять кастомную логику, описанную в теле конструктора.
Я спросил: "обязан ли по вашему конструктор всегда выполнять все эти действия"? Или он может выполнять только 3-й пункт? Если он выполняет только 3-й пункт, он остаётся конструктором или нет? Если он при этом "остаётся конструктором", то получется нет никакой разницы между им и простым инициализатором???

Ку? Обосновал? Дошло??? Так достаточно внятно, или нужно разжёвывать ещё доступнее?

Цитата Сообщение от eva2326 Посмотреть сообщение
Вы точно знаете, что такое статическая библиотека?
Из статической библиотеки в принципе нельзя выкидывать функции с внешним связыванием.
Причём тут какая-то "статическая библиотека" и какое она имеет отношение к теме данного обсуждения???
Ещё раз напоминаю, что в данной теме обсуждается инициализация объекта и как она отличается от конструктора объекта.
Если конструктор - пустая функция (типа изображённой вами выше) или функция не имеющая никакого полезного результата, то компилятор вправе полностью выкинуть её. И никакие "статические библиотеки" к делу не относятся - не нужно накидывать на вентилятор!

Цитата Сообщение от eva2326 Посмотреть сообщение
Свои слова я подкрепила кодом
Где именно??? Где именно код, относящийся к обсуждаемой теме. К теме инициализации объекта, а не какой-то "статической библиотеки" или прочей жизни на Марсе, непонятно зачем притянутой за уши.
Пока вы приводили только не относящийся к делу мусор.

Цитата Сообщение от eva2326 Посмотреть сообщение
Тут одно из двух: либо вы - балабол
Так перестаньте балаболить про "статические библиотеки" и пр. и наконец-то ответьте на вопрос про 3-й пункт.
Сможете? Или способны только хамить с дивана?
0
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,114
Записей в блоге: 2
21.05.2026, 04:07  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
C++
1
static_assert(std::is_trivially_copyable<T>::value, "Template type is not valid must be a trivial copyable");
А такая мелкая пакость
C++
1
2
3
struct MyClass {
 int * ptr;
};
0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
21.05.2026, 09:32
Igor3D, самый простой вариант: объявить пустой класс с говорящим именем, сделать каждый тип который предполагается сохранять таким образом его наследником и в ассерте (или в концепте) проверять является ли подставляемый тип наследником этого служебного класса.

C++
1
2
3
4
5
6
7
class StreamSerializable { };
 
static_assert(std::is_base_of_v<StreamSerializable, T>, "Template type is not valid must inherit  StreamSerializable");
 
struct MyClass : StreamSerializable {
    int * ptr;
};
Если у Вас предполагается сложная система типов относительно StreamSerializable (сиречь: если определяя очередной MyClass наследником StreamSerializable Вы не можете его финализировать)
C++
1
2
3
struct MyClass final : StreamSerializable {
    int * ptr;
};
то лучше сразу продумать другие варианты ограничения подставляемого типа.
0
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,114
Записей в блоге: 2
21.05.2026, 19:28  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
самый простой вариант: объявить пустой класс с говорящим именем, сделать каждый тип который предполагается сохранять таким образом его наследником и в ассерте (или в концепте) проверять является ли подставляемый тип наследником этого служебного класса.
Такой вариант не кажется ни простым ни хорошим. Запись для undo - далеко не единственный вариант сохранения/загрузки данных, есть еще I/O для файла проекта, для использования др приложением, текстовый и др. Поэтому навязывать многим классам базовый StreamSerializable - плохая идея

Не уловил чего Вы хотели достичь "финализацией"?
0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
21.05.2026, 19:55
Цитата Сообщение от Igor3D Посмотреть сообщение
Такой вариант не кажется
дело хозяйское
Цитата Сообщение от Igor3D Посмотреть сообщение
Запись для undo - далеко не единственный вариант
ну ктож знал...
Цитата Сообщение от Igor3D Посмотреть сообщение
навязывать многим классам базовый
это стандартная практика в ООП: создавать базовый класс чтобы построить от него одного наследника - идиотизм, от базовых классов строятся деревья наследования, если считаете это плохой идеей, дело Ваше, я её не продаю.
Цитата Сообщение от Igor3D Посмотреть сообщение
Не уловил чего Вы хотели достичь "финализацией"?
я ничего не хотел достичь, лишь пытался подсказать ответ на Ваш вопрос, такая вот странная причуда. Финализация в этом случае нужна чтобы случайно не получить сквозного наследования для класса который должен быть ограничен для применения в этом шаблоне.
0
1977 / 833 / 115
Регистрация: 01.10.2012
Сообщений: 5,114
Записей в блоге: 2
21.05.2026, 20:17  [ТС]
Цитата Сообщение от Annemesski Посмотреть сообщение
это стандартная практика в ООП: создавать базовый класс чтобы построить от него одного наследника - идиотизм, от базовых классов строятся деревья наследования, если считаете это плохой идеей, дело Ваше, я её не продаю.
Не раз встречал утверждение что тащить хоть какие-то подробности (де)сериализации (I/O) в рабочие/базовые классы не следует, наоборот, они понятия не должны иметь кто (или как, где) их будет читать/писать. Т.е. запись/чтение должны быть абсолютно "левыми" (независимыми). Согласен с этим утверждением, хотя и возникает неудобный момент доступа к private

Ну хорошо, а отловить член-указатель - никак?
0
 Аватар для Annemesski
2689 / 1346 / 485
Регистрация: 08.11.2016
Сообщений: 3,734
21.05.2026, 20:41
Цитата Сообщение от Igor3D Посмотреть сообщение
Не раз встречал утверждение что тащить хоть какие-то подробности (де)сериализации (I/O) в рабочие/базовые классы не следует
это верное утверждение, которое непонятно каким образом относится к предложенному StreamSerializable, который не содержит никаких деталей, а служит ровно одной цели - наложению ограничений.
Цитата Сообщение от Igor3D Посмотреть сообщение
отловить член-указатель - никак?
есть std::is_pointer<T> который скажет указатель это или нет, но чтобы его применить для такой задачи надо как-то разложить класс на поля и как-то по ним пройтись, а в плюсах с рефлексиями туго, почти уверен что сделать можно, но выглядеть это будет как коричневая магия.
0
 Аватар для 4blK4blPblK
16 / 12 / 3
Регистрация: 01.10.2023
Сообщений: 106
22.05.2026, 03:03
Цитата Сообщение от Igor3D Посмотреть сообщение
чисто практическая задачка
В контексте темы? Не улавливаю ее суть, но де/сериализацию делал бы переопределением операторов "<<" и ">>" - на этапе компиляции покажет, можно ли читать/писать тип(класс) в Stream (что это за стрим?). Если же в контексте темы - то я хз.

Цитата Сообщение от Igor3D Посмотреть сообщение
а отловить член-указатель - никак?
Так же хз, в тему/не в тему - но вспомнил давние примеры доступа к приватным членам от кого-то из здешних гуру (ники авторов и названия тем, к сожалению, не сохранил)
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// пример 1 
#include <iostream>
 
class Foo {
    int field;
    void print() { std::cout << field << '\n'; }
};
 
inline int Foo::* Foo_field_ptr;
inline void (Foo::* Foo_print_ptr)();
 
template<auto* Ptr, auto Val>
class SetVar {
    static const bool _;
};
 
template<auto* Ptr, auto Val>
inline const bool SetVar<Ptr, Val>::_ = (*Ptr = Val, false);
//                              оператор "запятая" ^
 
template class SetVar<&Foo_field_ptr, &Foo::field>;
template class SetVar<&Foo_print_ptr, &Foo::print>;
 
int main() {
    Foo x;
    x.*Foo_field_ptr = 42;
    (x.*Foo_print_ptr)();
}
 
 
 
// пример 2
 
#include <iostream>
 
class Foo 
{
    int field;
    void print() { std::cout << field << '\n'; }
};
 
// Привязывает член класса к некоторому типу-тегу,
// и определяет функцию, которая возвращает указатель на член класса.
template<typename Tag, auto MemPtr>
class PrivateMemberAccess {
    friend constexpr auto GetPrivateMemPtr(Tag) {
        return MemPtr;
    }
};
 
// Всмомогательный тип чтобы писать <T> вместо (T{})
template<typename Tag>
static constexpr auto PrivateMemberPtr = GetPrivateMemPtr(Tag{});
 
 
struct Foo_field_tag {};
struct Foo_print_tag {};
 
template class PrivateMemberAccess<Foo_field_tag, &Foo::field>;
template class PrivateMemberAccess<Foo_print_tag, &Foo::print>;
 
// Если не объявить функцию, то мы не сможем её вызвать.
constexpr auto GetPrivateMemPtr(Foo_field_tag);
constexpr auto GetPrivateMemPtr(Foo_print_tag);
 
int main() {
    Foo x;
    x.*PrivateMemberPtr<Foo_field_tag> = 42;
    (x.*PrivateMemberPtr<Foo_print_tag>)();
}
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
23.05.2026, 00:57
Цитата Сообщение от Annemesski Посмотреть сообщение
в плюсах с рефлексиями туго
Рефлексию в с++26 добавили.

Например, вот так можно пробежаться по всем полям произвольной структуры, и вывести их имена и значения:
(Актуально для любителей сериализаторы колхозить)


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <string_view>
#include <iostream>
#include <string>
#include <meta> 
 
template <typename T>
void print_structure_fields(const T& obj)
{
    constexpr auto ctx = std::meta::access_context::unchecked();
    
    static constexpr auto fields = std::define_static_array(
        std::meta::nonstatic_data_members_of(^^T, ctx)
    );
    
    template for (constexpr auto field : fields) 
    {
        constexpr std::string_view field_name = std::meta::identifier_of(field);
        auto field_value = obj.[:field:];
        std::cout << field_name << ": " << field_value << "\n";
    }
}
 
struct User 
{
    int id;
    std::string name;
    double rating;
};
 
struct Point 
{
    double x;
    double y;
    double z;
};
 
int main()
{
    User user{ .id = 101, .name = "Константин", .rating = 4.95 };
    std::cout << "=== Данные пользователя ===\n";
    print_structure_fields(user);
 
    std::cout << "\n";
 
    Point pt{ .x = 1.0, .y = 2.5, .z = -3.7 };
    std::cout << "=== Координаты точки ===\n";
    print_structure_fields(pt);
}
Вывод в консоль:
Code
1
2
3
4
5
6
7
8
9
=== Данные пользователя ===
id: 101
name: Константин
rating: 4.95
 
=== Координаты точки ===
x: 1
y: 2.5
z: -3



А ещё, теперь можно легально проверить тип на полноту, без риска налететь на UB

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <meta>
 
struct some; 
 
constexpr std::meta::info info_1 = ^^some;
constexpr bool check_1 = std::meta::is_complete_type(info_1);
 
struct some{};
 
constexpr std::meta::info info_2 = ^^some;
constexpr bool check_2 = std::meta::is_complete_type(info_2);
 
int main() 
{
    std::cout << std::boolalpha;
    std::cout << "check_1: " << check_1 << "\n";
    std::cout << "check_2: " << check_2 << "\n";
}
Раньше такое де-факто работало только начиная с с++20, за счет использования consteval
Но де-юре, код использующий трюк с consteval все равно Ill-formed, no diagnostic required, что по сути является UB
Но поскольку все инстансы, и прочие компалтайм вычисления, которые порождаются в процессе работы consteval, должны быть аннигилированы по факту её завершения, то технически нарушения ODR не возникает (ед. трансляции не порождают конфликтующих символов для линкера).

В некотором смысле, рефлексия просто узаконила подобные трюки.
Появилось новое понятие: Evaluation Context
Конечно, само по себе понятие manifestly constant-evaluated context и раньше было, но в с++26 его радикально пересмотрели.
Раньше контекст определял только "мы в рантайме" или "мы времени компиляции".
Новый контекст теперь содержит "состояние компалтайм-вычислений здесь и сейчас".
Теперь можно выстраивать логику по принципу "сейчас состояние кодовой базы одно, а на следующем шаге оно может стать другим"

Пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <vector>
#include <meta>
 
// 1. Объявляем структуру как НЕПОЛНЫЙ тип (0 полей)
struct MyData;
 
// Функция-шаблон для проверки завершенности. 
consteval bool check_completeness()
{
    // is_complete_type легально принимает неполные типы на вход
    return std::meta::is_complete_type(^^MyData);
}
 
// --- Момент времени 1
// Проверяем тип в глобальном контексте ДО его наполнения полями
constexpr bool state_1 = check_completeness();
 
// --- момент времени 2 (Мутация типа)
// Глобальный consteval-блок. 
consteval
{
    std::vector<std::meta::info> fields;
    fields.push_back(std::meta::data_member_spec(^^int, {.name = "x"}));
    fields.push_back(std::meta::data_member_spec(^^float, {.name = "y"}));
    
    // Наполняем глобальный тип MyData полями
    std::meta::define_aggregate(^^MyData, fields);
}
 
// --- Момент времени 3
constexpr bool state_3 = check_completeness();
 
int main() 
{
    std::cout << std::boolalpha;
    std::cout << "Is MyData complete at Point 1: " << state_1 << "\n"; // Выведет: false
    std::cout << "Is MyData complete at Point 3: " << state_3 << "\n"; // Выведет: true
 
    // Теперь, когда тип гарантированно полный, мы можем безопасно узнать число полей
    constexpr std::meta::access_context ctx = std::meta::access_context::current();
    constexpr size_t final_fields = std::meta::nonstatic_data_members_of(^^MyData, ctx).size();
    std::cout << "Final fields count: " << final_fields << "\n"; // Выведет: 2
 
    // Объектом тоже можно пользоваться
    MyData instance{.x = 10, .y = 3.14f};
    std::cout << "Values: " << instance.x << ", " << instance.y << "\n";
}

Вывод в консоль:
Code
1
2
3
4
Is MyData complete at Point 1: false
Is MyData complete at Point 3: true
Final fields count: 2
Values: 10, 3.14
3
Вездепух
Эксперт CЭксперт С++
 Аватар для TheCalligrapher
13210 / 6843 / 1824
Регистрация: 18.10.2014
Сообщений: 17,306
29.05.2026, 23:49
Цитата Сообщение от Igor3D Посмотреть сообщение
Хорошо, вот слышал что можно создать объект минуя вызов его конструктора, хотя таковой имеется.
Если формально-педантично трактовать постановку задачи, то она имеет простое и очевидное решение

C++
1
struct S { int x; } s = { 42 };
У класса S есть конструкторы: конструктор по умолчанию и конструктор копирования. Однако в данном примере объект инициализируется при помощи агрегатной инициализации, которая не использует никаких конструкторов.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
29.05.2026, 23:49

Передача файлов от клиента к клиенту, минуя сервер(как в icq).
Доброго времени суток! Имеется простенький сервер и клиент, основанные на сокетах(TClientSocket...

Получение пикселей текста минуя HDC
Сабж. Можно ли вообще получить напрямую буфер пикселей (скажем в 32 битном режиме пикселя) типа...

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

Как побитово скопировать uint32_t в float, минуя при этом выделения uint32_t в оперативную память
Всем привет, недавно заинтересовал такой вопрос ниже. Как ПОБИТОВО скопировать uint32_t в float,...

Создать объект стек и создать объект очередь с перегруженными операциями
Помогите, пожалуйста с заданиями. 1. Унарная операция Создать объект стек, перегрузив ++ и --...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
сукцессия 29. Переход от одних деревьев на другие делать более или менее вероятностным?
anaschu 12.07.2026
Насколько смена типов микоризы — исключительное событие в двухвековой сукцессии? Оценка вероятности в пространстве параметров В текущей версии модели успешно реализован ключевой механизм. . .
сукцессия 27. Думаю, как переделывать уже написанную статью с планами на сукцессию.
anaschu 12.07.2026
Анализ соответствия модели требованиям Реализованные компоненты: Механизм закисления почвы через протонную помпу Конкуренция между типами микориз pH как триггер сукцессии C/ P соотношение. . .
Сукцессия 26. Мат модель создана.
anaschu 12.07.2026
Модель смены растительных сукцессий посредством управления грибами работает внутри небольшой ячейки почвы, восстанавливающейся после пожара, где ненадолго бывшее царство хвойных снова захватили. . .
Решил проблему с ошибкой пагинации сообщений с сервера на алгоритме обхода дерева "Эстафета хвоста".
Hrethgir 12.07.2026
Проблема была в том, что удалялась именно новая кнопка, а не старая. Ни один ИИ не обнаружил это, а сам я смог только когда с работой стало попроще и когда заставил работать будущее автономное. . .
сукцессия 25. Хронология ошибок
anaschu 12.07.2026
# От 50-тонного гриба до устойчивого леса: хроника ошибок при построении модели вековой сукцессии микоризы ## О чём эта статья В процессе построения ОДУ-модели (система дифференциальных. . .
сукцессия 24. Промежуточное общее описание модели
anaschu 12.07.2026
Хендофф: модель АМ→ЭКМ сукцессии микоризы (ризосфера, 50 лет) Содержание проекта Симуляция вековой (50 лет) экологической сукцессии в почве леса Основные участники: АМ-гриб, ЭКМ-гриб,. . .
сукцессия 23. Более физиологичная физиология, более экологичная экология, более диффурные диффуры.
anaschu 12.07.2026
Что реально нашли и починили за эти 5 часов Правило Линдемана (КПД конверсии сахара в тело, kEff) — раньше 100% полученного углерода шло прямо в биомассу гриба; теперь только kEff=0. 5 (после. . .
сукцессия 22. От артефактов к физиологии: калибровка агентной модели грибной сукцессии для воспроизведения сезонной динамики и pH-плато
anaschu 11.07.2026
Аннотация В данной работе представлена калибровка агентной модели динамики грибных сообществ (fungal-succession), направленная на устранение нефизичных артефактов (коллапс биомассы, мгновенное. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru