Извлечь шаблон контейнера для хранения другого типа

Учитывая, что на входе находится контейнер определенного типа, можно ли «извлечь» шаблон контейнера и создать его экземпляр для другого типа? Например:

void f ( auto& x ) {
  getContainerTemplate(x)<int> a (10); // getContainerTemplate() 
  // is some magic function.
}

Эффект f() должен быть следующим в зависимости от типа x:

void f ( std::vector<double>& x ) {
  std::vector<int> a(10);
}

или

void f ( parlay::sequence<double>& x ) { // parlay::sequence is
  // another container template storing elements contiguously.
  parlay::sequence<int> a(10);
}

Небольшая проблема заключается в том, что std::vector<double> на самом деле является std::vector<double, std::allocator<double>>. Достаточно легко превратить std::vector<double, std::allocator<double>> в std::vector<int, std::allocator<double>>, но тогда это, конечно, не скомпилируется. Мне не сразу понятно, как вместо этого превратить его в std::vector<int, std::allocator<int>>, без жесткого кодирования конкретных знаний о деталях std::vector.

Igor Tandetnik 11.07.2024 01:55

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

Igor Tandetnik 11.07.2024 01:56

Опубликуйте это как ответ @IgorTandetnik. Я пытался найти похожее решение, но оно было не таким чистым.

Stephen Newell 11.07.2024 01:59

@Игорь Тандетник - вы можете использовать allocator_traits для повторной привязки нового распределителя

Gene 11.07.2024 04:34

@Gene Да, но вам нужно будет жестко запрограммировать тот факт, что второй параметр на самом деле является распределителем, тем самым теряя общность.

Igor Tandetnik 11.07.2024 05:20
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
5
85
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Что-то в этом роде, возможно:

template <typename T, typename C>
struct ContainerTypeReplacer;

template <typename T, template <typename...> class C, typename... Args>
struct ContainerTypeReplacer<T, C<Args...>> {
    using type = C<T>;
};

template <typename T, typename C>
typename ContainerTypeReplacer<T, C>::type getContainerTemplate(const C&);

Использование:

void f (auto& x) {
  // if x is of type e.g. std::vector<double>,
  // then a is std::vector<int>
  decltype(getContainerTemplate<int>(x)) a(10);
}

Демо

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

Да, это возможно. Использование «параметра шаблона шаблона» (см. Как можно использовать параметры шаблона шаблона?) вместо auto позволит f() определить тип шаблона, экземпляр которого затем можно создать с помощью собственных параметров, например:

template< template<typename, typename...> class Container, typename T >
void f ( Container<T>& x ) {
  Container<int> a(10);
  ...
}

Онлайн-демо

Каков структурированный подход к освоению метапрограммирования шаблонов?

user2961927 11.07.2024 05:41

Я не могу на этот вопрос ответить, так как я самоучка. Все, что я могу сказать, это изучать код других людей и практиковаться.

Remy Lebeau 11.07.2024 08:44

Начиная с С++ 20, у вас есть концепции, которые позволяют вам работать с одной и той же функцией шаблона с разными типами. Это может помочь повысить безопасность работы с различными типами. Например, проверьте std::convertible_to<T>, std::same_as<T> и std::is_floating_point_v<T>.

Какое это имеет отношение к вопросу?

cigien 11.07.2024 09:01

@cigien можно ли «извлечь» шаблон контейнера и создать его экземпляр для другого типа? - Мой ответ содержит более безопасный вариант «извлечения» некоторых типов/типов из командной таблички.

Alexandr Gritsenko 11.07.2024 14:48

Достичь совершенства сложнее, чем кто-либо здесь признает.

Простой параметр шаблона шаблона работает в 80% случаев, но не в остальных. Например, если вы хотите сделать это с картами, это не сработает, потому что ваш компаратор может не совпадать.

Возможно, вам стоит подумать о том, чтобы превратить ваши контейнеры в моноиды в смысле Haskell. Эти моноиды описывают, как выполнять над ними определенные операции. В C++ вы публикуете эти операции через классы признаков; мы можем попытаться заставить его работать, используя общие шаблоны, а затем улучшить его для конкретных подтипов.

Самый простой способ — написать fmap пункт настройки. fmap принимает моноид и функцию и создает новый моноид с содержимым моноида, отображаемым функцией.

В контейнере это делает примерно то, что вы хотите.

Поддерживать базовые контейнеры последовательностей легко; затем вы можете расширить его для поддержки ассоциативных контейнеров. Это может пойти гораздо дальше; вы можете поддерживать интеллектуальные указатели, дополнительные параметры, моноиды ввода-вывода (представляющие «вычисления, которые необходимо выполнить после ввода пользователем данных»), фьючерсы и т. д.

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