Есть ли способ свернуть список инициализаторов в С++ 17

Есть ли способ свернуть список инициализаторов вместо использования пакета параметров? Моя проблема в том, что у меня сильно перегруженный конструктор, и я хочу вызывать разные конструкторы в зависимости от того, использую ли я {} или нет. Кажется, это отлично работает со списком инициализаторов, которому удается скрыть мой другой конструктор с одним аргументом, когда я использую {}, в отличие от того, когда я просто создаю его с помощью (), но терпит неудачу, если я использую пакет параметров, который не скрывает мой другой конструктор аргументов.

Кроме того, я видел, как люди добавляют пустоту в свои выражения свертывания, что я не мог понять, когда ссылался на cppreference, и, похоже, это не имеет никакого значения в моей программе.

Редактировать: Как и просили, пример для иллюстрации проблемы:

#include <iostream>
#define USE_PARAMETER_PACK false

template<typename T>
struct Mega
{
    int d;
    T* arr;
    Mega(int d) : d(d), arr(new T[d]) {}

    Mega(int d, T u) : d(d), arr(new T[d])
    {
        std::fill(arr, arr + d, static_cast<T>(u));
    }
#if USE_PARAMETER_PACK == true
    template<typename ...Ts>
    Mega(Ts&& ... vals) : d(sizeof...(Ts)), arr(new T[sizeof...(Ts)])
    {
        // fills the array with the arguments at compile time
        int i = 0;
        (void(arr[i++] = static_cast<T>(vals)), ...);
    }
#else
    template<typename U>
    Mega(const std::initializer_list<U>& list) : d(list.size()), arr(new T[d])
    {
        auto it = list.begin();
        //int i = 0;
        //((arr[i++] = (list)), ...);
        for (size_t i = 0; i < d; ++i, ++it)
            arr[i] = static_cast<T>(*it);
    }
#endif
};

template<typename T>
std::ostream& operator<<(std::ostream& os, const Mega<T>& m)
{
    for (size_t i = 0; i < m.d; ++i)
        os << m.arr[i] << "\t";
    return os;
}

int main()
{
    int* k;
    k = new int[2];
    k[0] = 2;
    k[1] = 3;
    Mega<int> l( k[0] );
    // hides 1 argument ctor through {} invocation if using initializer_list,
    // not so with parameter pack
    Mega<int> m({ k[0]}); 
    Mega<int> n(k[0], k[1]);
    // hides 2 argument ctor through {} invocation if using initializer list
    // not so with parameter pack
    Mega<int> o({ k[0], k[1] }); 
    std::cout << l << "\n";
    std::cout << m << "\n";
    std::cout << n << "\n";
    std::cout << o << "\n";

    return 0;
}

Обратите внимание на закомментированную часть, мне бы хотелось иметь возможность сделать что-то подобное, чтобы процесс заполнения списков параметров известного размера можно было выяснить во время компиляции, а не использовать цикл for. Должен выводить несколько мусорных значений для первого cout и 2 для второго (по крайней мере, в MSVC2017 это так, не знаю, соответствует ли этот механизм скрытия стандарту). Обратите внимание, что если вы установите для определения значение true, вы можете использовать ctor пакета параметров, но он не может скрыть ctor с одним аргументом даже с синтаксисом {}.

Edit2: дополнительно обновил код для максимального удобства, просто измените определение на true, чтобы увидеть, что пакет параметров не может скрыть конструкторы аргументов 1 и 2 с синтаксисом {}, тогда как ctor списка инициализаторов справляется.

Ссылки: Использование списка инициализаторов: http://coliru.stacked-crooked.com/a/7b876e1dfbb18d73 Выход:

0   0   

2   

3   3   

2   3   

Использование пакета параметров: http://coliru.stacked-crooked.com/a/11042b2fc45b5259 Выход:

0   0   

0   0   

3   3   

3   3   

Кто-то может помочь вам, если вы опубликуете минимальный воспроизводимый пример.

R Sahu 07.04.2019 22:42

Что вы подразумеваете под «сворачиванием списка инициализаторов»?

Barry 07.04.2019 22:43

@Barry Возможность свернуть список инициализаторов так же, как пакет параметров.

lightxbulb 07.04.2019 22:46

Термин void в сгибе предназначен для запрета вызова определенных пользователем перегрузок операторов (особенно оператора запятой), поскольку параметры функции не могут быть cv-квалифицированными как void. Вероятно, это не проблема для большинства пользователей, но необходима для правильного и безопасного кода библиотеки.

Cruz Jean 07.04.2019 22:51
"Мне бы хотелось иметь возможность сделать что-то подобное, чтобы... процесс... можно было понять во время компиляции" Я бы не беспокоился об этом. Современные компиляторы должны быть достаточно умны, чтобы развернуть цикл.
HolyBlackCat 08.04.2019 00:08

Почему бы вам не использовать std::tuple<> ??

Michaël Roy 08.04.2019 04:07

@MichaëlRoy Не могли бы вы уточнить, как это поможет в этом случае? Будет ли синтаксис {} скрывать другие ctor с кортежем?

lightxbulb 08.04.2019 09:00

Нет, мой комментарий больше касается устранения петли. Кортежи могут быть созданы из списка инициализаторов. Чистый результат был бы таким же, но с бонусной распаковкой, предоставляемой stl. Заметки в стороне: вам действительно следует сделать свой единственный аргумент ctor явным. А почему вы не используете std::vector??? По возможности избегайте прямых вызовов new и delete. Это также сделает вашу жизнь намного проще.

Michaël Roy 08.04.2019 09:27

@MichaëlRoy О, хорошо, я тебя понял. Я знаю, как написать рекурсивный шаблон для списков инициализации, мне просто интересно, работает ли свертывание, что, я думаю, не основано на ответах. Кроме того, почему я должен запрещать неявные преобразования аргументов, особенно если типы совместимы? Я не использую std::vector, потому что остальная часть кода, которую вы здесь не видите, делает то, чего не может std::vector.

lightxbulb 08.04.2019 10:07

неявные конструкторы с одним аргументом — это ошибки, ожидающие своего появления. И я занимаюсь этим достаточно долго, чтобы заверить вас, что нет операции, которую вы можете сделать с массивом, которую вы не можете сделать с вектором. Цель состоит в том, чтобы устранить возможные ошибки. Хорошая практика кодирования — это предотвращение головной боли позже

Michaël Roy 08.04.2019 11:39

@MichaëlRoy Да, мне, вероятно, следует сделать один аргумент ctor, а не явным, принимать только целочисленные типы без знака, а также иметь утверждение для 0 в качестве аргумента. С другой стороны, я должен, что неявные аргументы не всегда являются ошибкой, рассмотрим математический векторный класс (фиксированное измерение), который можно инициализировать из скаляра (все координаты становятся равными ему), он не должен быть явным для удобства (смотрите например, на gsl или hlsl). Что касается использования вектора, к нему прилагается слишком много ненужного в моем случае, и это на самом деле немного усложняет ситуацию для моего конкретного класса.

lightxbulb 08.04.2019 15:17

Конструкторы с одним аргументом сложны. Эмпирическое правило состоит в том, чтобы объявить их явными, если два типа не выражают одну и ту же идею. Например, конструктор, принимающий std::vector (подсказка к вашей проблеме), не должен быть явным. Но неявный ctor, принимающий одно целое, опасен, а также может сбивать с толку в точке вызова. my_mega = some_int; может быть немного запутанным, а my_mega = Mega{some_int}; `не может быть. Поищите эту хорошую лекцию, которая затрагивает эту тему; «CppCon 2018: Титус Винтерс «Современный дизайн C++»

Michaël Roy 08.04.2019 21:10

продолжение подсказки.... Имея ctor, принимающий стандартный вектор по стоимости, ваша строительная площадка станет auto my_mega = Mega<int>{{1, 2, 3}},, и если бы у вас был вектор для хранения данных вашего объекта, вы даже могли бы (в зависимости от типа help во входном векторе ) для вызова конструктора перемещения вашего внутреннего вектора с предоставленным пользователем, тем самым сохраняя выделение памяти.

Michaël Roy 09.04.2019 20:52

Я рассмотрю использование std::vector, я работал над полностью статической реализацией (со статическим размером) и в значительной степени сумел реализовать все операции как constexpr.

lightxbulb 09.04.2019 22:01

Списки инициализаторов имеют размер, выбираемый во время выполнения; их складывание — это дело цикла, а не обработчика шаблонов. (Я не могу точно сказать, является ли это отвечать чем-то большим, чем просто заголовок вопроса.)

Davis Herring 02.05.2019 04:28

@DavisHerring Это действительно хороший ответ, он подтверждает, что для них просто невозможно выполнить статическое складывание.

lightxbulb 02.05.2019 06:57

@lightxbulb: он не решает проблему сокрытия конструктора, но я опубликую его как ответ, если хотите.

Davis Herring 02.05.2019 15:06

@DavisHerring Я думаю, что часть конструктора очевидна, поскольку одна является объектом, а другая представляет собой набор аргументов, но не стесняйтесь добавлять ее в качестве ответа.

lightxbulb 02.05.2019 15:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
18
450
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку длина initializer_list равна Неизвестный во время компиляции (в частности, разные сайты вызовов с разным количеством элементов вызывают одну и ту же функцию), любая constexpr обработка выполняется за невозможный. (Соответствующие функции-члены являютсяconstexpr, но вы не можете использовать их как таковые в параметре функции.) В частности, для выражения fold требуется пакет параметров, размер которого всегда является выражением-константой.

Другие вопросы по теме