Я новичок в концепциях шаблонов, таких как SFINAE
или tag dispatching
, и читал несколько статей и примеров об этом, которые не помогли мне понять мой подход. Так что я был бы очень признателен, если бы кто-нибудь мог помочь.
Моя цель состоит в том, чтобы иметь одну единственную функцию синтаксического анализа, которая будет делать некоторые вещи перед передачей данных другим функциям для выполнения определенного анализа в зависимости от типа шаблона T
.
В прилагаемом коде я хочу добиться именно такого поведения. Я использую здесь if constexpr
, который, к сожалению, является функцией C ++ 17, которая недоступна в версии C ++, которую я использую.
Я думаю, что для этой цели на первый взгляд кажется, что template specialization
было бы лучшим решением, но это не то, что я хотел.
Я думаю, что для этой цели tag dispatching
было бы хорошим направлением, но я не уверен, как именно это сделать с type_traits
, когда у меня есть пользовательские типы, поскольку у меня всегда есть 2 варианта, { {X5}} или false_type
, но в следующем коде у меня есть 3 ситуации, в которых потенциально может быть больше.
Я был бы очень признателен за несколько примеров или указаний, как лучше всего делать то, что я ищу. Было бы здорово прочитать даже какую-нибудь статью.
Заранее спасибо!
Пример рабочего кода:
#include <string>
#include <vector>
#include <memory>
using namespace std;
struct Base { int id; };
struct Foo : Base { int fooValue; };
struct Bar : Base { int barValue; };
shared_ptr<Foo> parseFoo(const string & data) { return make_shared<Foo>(); }
shared_ptr<Bar> parseBar(const string & data) { return make_shared<Bar>(); }
shared_ptr<vector<Foo>> parseFoos(const string & data) { return make_shared<vector<Foo>>(); }
template <typename T>
shared_ptr<T> parse(const std::string & data)
{
shared_ptr<T> result = nullptr;
if (data.empty())
return result;
result = make_shared<T>();
if constexpr (std::is_same<T, Foo>::value) result = parseFoo(data);
else if constexpr (std::is_same<T, Bar>::value) result = parseBar(data);
else if constexpr (std::is_same<T, vector<Foo>>::value) result = parseFoos(data);
return result;
}
int main()
{
string data = "some json response";
auto foo = parse<Foo>(data);
auto bar = parse<Bar>(data);
auto foos = parse<vector<Foo>>(data);
return 0;
}
2 ответа
Отправка тегов здесь упростит:
struct Base { int id; };
struct Foo : Base { int fooValue; };
struct Bar : Base { int barValue; };
template <typename T> struct Tag {};
std::shared_ptr<Foo> parse_impl(Tag<Foo>, const std::string& data) { return make_shared<Foo>(); }
std::shared_ptr<Bar> parse_impl(Tag<Bar>, const std::string& data) { return make_shared<Bar>(); }
std::shared_ptr<std::vector<Foo>> parse_impl(Tag<std::vector<Foo>>, const std::string& data)
{
return make_shared<std::vector<Foo>>();
}
template <typename T>
std::shared_ptr<T> parse(const std::string& data)
{
if (data.empty())
return nullptr;
return parse_impl(Tag<T>{}, data);
}
Почему бы вам просто не указать специализацию шаблона для parseFoo
, parseBar
и parseFoos
, а затем просто вызвать метод шаблона из статической функции parse
:
//parseT replaces parseFoo, parseBar, parseFoos
template<typename T>
std::shared_ptr<T> parseT(const std::string & data);
// provide implementaiton for Foo, Bar and vector<Foo>
template<>
std::shared_ptr<Foo> parseT<Foo>(const std::string & data) {
return std::make_shared<Foo>();
}
template<>
std::shared_ptr<Bar> parseT<Bar>(const std::string & data) {
return std::make_shared<Bar>();
}
template<>
std::shared_ptr<std::vector<Foo>> parseT<std::vector<Foo>>(const std::string & data) {
return std::make_shared<std::vector<Foo>>();
}
template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
std::shared_ptr<T> result = nullptr;
if (data.empty())
return result;
result = std::make_shared<T>();
result = parseT<T>(data); // simple call to template function
return result;
}
РЕДАКТИРОВАТЬ: Упс, недостаточно читал, теперь я вижу, что это не то, что вы хотели (хотя не совсем уверен, почему, это, кажется, лучший вариант для меня: D). В любом случае, если вы хотите использовать тег, отправляющий что-то в соответствии со строками следующего кода, мне приходит в голову (опять же, IMO не так уж и хорошо из-за другого параметра шаблона для функции parser
):
struct FooTag {};
struct BarTag{};
struct FoosTag{};
std::shared_ptr<Foo> parseT(const std::string & data, FooTag) {
return std::make_shared<Foo>();
}
std::shared_ptr<Bar> parseT(const std::string & data, BarTag) {
return std::make_shared<Bar>();
}
std::shared_ptr<std::vector<Foo>> parseT(const std::string & data, FoosTag) {
return std::make_shared<std::vector<Foo>>();
}
// template version
template <typename T, typename Tag>
std::shared_ptr<T> parser(const std::string & data) {
std::shared_ptr<T> result = nullptr;
if (data.empty())
return result;
result = std::make_shared<T>();
result = parseT(data, Tag());
return result;
}
Если вам не нужен дополнительный параметр шаблона, вы можете позволить пользователю указать класс тега внутри Foo
и Bar
и других элементов, но это не будет работать, если у вас есть vector
{{ X3 }} с :
// Tag is now a nested class
class Foo {
public:
struct Tag{};
};
class Bar {
public:
struct Tag{};
};
std::shared_ptr<Foo> parseT(const std::string & data, Foo::Tag) {
return std::make_shared<Foo>();
}
std::shared_ptr<Bar> parseT(const std::string & data, Bar::Tag) {
return std::make_shared<Bar>();
}
template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
std::shared_ptr<T> result = nullptr;
if (data.empty())
return result;
result = std::make_shared<T>();
result = parseT(data, T::Tag()); // tag is now inside of template parameter
return result;
}
Другой РЕДАКТИРОВАТЬ: Вы можете создать шаблонный класс для тега, чтобы избавиться от лишнего параметра шаблона в функции parser
template <typename T>
struct Tag{};
std::shared_ptr<Foo> parseT(const std::string & data, Tag<Foo>) {
return std::make_shared<Foo>();
}
std::shared_ptr<Bar> parseT(const std::string & data, Tag<Bar>) {
return std::make_shared<Bar>();
}
template <typename T>
std::shared_ptr<T> parser(const std::string & data) {
std::shared_ptr<T> result = nullptr;
if (data.empty())
return result;
result = std::make_shared<T>();
result = parseT(data, Tag<T>{});
return result;
}
Новые вопросы
c++
C ++ - это язык программирования общего назначения. Первоначально он был разработан как расширение C и имеет аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде (который будет скомпилирован с помощью компилятора C ++). Используйте тег, зависящий от версии, для вопросов, связанных с конкретной редакцией стандарта [C ++ 11], [C ++ 14], [C ++ 17] или [C ++ 20] и т. Д.