Можно ли привести к шаблонному классу без информации о типе?

Предположим, что у меня есть такой шаблонный класс:

template<typename Fn>
struct S {
  S(Fn fn) : fn_(fn) {}

  const auto& fn() const {
    return fn_;
  }
  Fn fn_;
}

Бывают случаи, когда вам нужно передать свой объект как void*, поэтому, если у меня есть такой указатель:

void* ptr = new S{[]() { std::cout << "hi!\n";}};

Все в порядке. Но проблема в том, что если я попытаюсь привести ptr обратно в свой класс, чтобы получить доступ к fn() методу, мне понадобится информация о типе. Поэтому следующий код не скомпилируется:

auto s = static_cast<S*>(ptr);

Теперь мой вопрос заключается в том, существует ли шаблон или метод для переноса информации о типе вместе с данными, чтобы сделать это приведение возможным или нет? Использование любого подхода/шаблона, который возвращает правильный экземпляр, в порядке. Варианты использования этой проблемы — когда вы работаете с библиотеками, которые передают контекст между методами как void*. Если вы хотите использовать шаблонный класс в качестве этого контекста, возникает эта проблема.

Есть идеи?

std::function делает это. Это называется стиранием типа.
tkausl 21.02.2023 00:30
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Калькулятор CGPA 12 для семестра
Калькулятор CGPA 12 для семестра
Чтобы запустить этот код и рассчитать CGPA, необходимо сохранить код как HTML-файл, а затем открыть его в веб-браузере. Для этого выполните следующие...
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
ONLBest Online HTML CSS JAVASCRIPT Training In INDIA 2023
О тренинге HTML JavaScript :HTML (язык гипертекстовой разметки) и CSS (каскадные таблицы стилей) - две основные технологии для создания веб-страниц....
Как собрать/развернуть часть вашего приложения Angular
Как собрать/развернуть часть вашего приложения Angular
Вам когда-нибудь требовалось собрать/развернуть только часть вашего приложения Angular или, возможно, скрыть некоторые маршруты в определенных средах?
Запуск PHP на IIS без использования программы установки веб-платформы
Запуск PHP на IIS без использования программы установки веб-платформы
Установщик веб-платформы, предлагаемый компанией Microsoft, перестанет работать 31 декабря 2022 года. Его закрытие привело к тому, что мы не можем...
Оптимизация React Context шаг за шагом в 4 примерах
Оптимизация React Context шаг за шагом в 4 примерах
При использовании компонентов React в сочетании с Context вы можете оптимизировать рендеринг, обернув ваш компонент React в React.memo сразу после...
0
1
55
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я вижу несколько вариантов:

  1. Наследуйте каждый класс от базового класса и используйте динамический полиморфизм.

  2. Используйте стандартную::функцию

  3. Используйте std::variant

  4. Используйте стандартный::любой

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

На самом деле я знал все эти методы, и ни один из них не работал у меня. мне нужна была концепция type easure, упомянутая в комментарии. Во всем, что вы предложили, мне нужно знать тип в конце концов.

Afshin 21.02.2023 01:13

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

Nicol Bolas 21.02.2023 01:17
Ответ принят как подходящий

Основная проблема здесь заключается в том, что вы хотите использовать лямбду в качестве параметра шаблона типа, а затем вы хотите иметь возможность преобразовать ее в/из void*. Тип лямбды неизвестен, сгенерирован компилятором. И даже если бы он был известен, вы бы не смогли его напечатать.

Поэтому вместо этого вам нужно удалить тип из S и засунуть его в конструктор S:

struct S {
  template<typename Func>
  S(Func &&fn) : fn_(std::forward<Func>(fn)) {}

  auto const& fn() const {
    return fn_;
  }

  std::function<void()> fn_;
}

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

Да, этот подход возможен только в том случае, если, как вы упомянули, вы знаете тип в конце или можете встроить его в другой известный тип. Моя проблема именно в том, что я не знаю тип (поскольку я не хотел привязывать свой код к конкретному типу) и не могу найти способ встроить его в другой тип (этот случай возможен с std::function, но вы можете легко найти случай, который не является). Я подумал, может быть, есть способ сохранить тип в базовом классе и использовать его, но, похоже, это не так.

Afshin 21.02.2023 01:29

@Afshin: вы можете использовать производные от шаблона классы базового класса; это распространенный способ реализации стирания типов. Но вы не воссоздаете первоначальный тип; вы просто помещаете virtual функции в базовый класс, который реализуют производные классы шаблонов, и вызываете эти virtual функции.

Nicol Bolas 21.02.2023 01:51

Да, проблема в том, что у вас не может быть auto в качестве возвращаемого типа метода virtual в базе. Представьте, что вы хотите, чтобы виртуальный метод получения в базовом классе возвращал шаблонную переменную-член в производном классе, что-то вроде этого: virtual auto get_x() const = 0; что неверно. Если вы знаете типы, вы можете использовать что-то вроде virtual std::string get_x() const = 0; в базовом классе, поэтому нам все равно нужно знать тип, и он не будет полностью универсальным.

Afshin 21.02.2023 01:57

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