2

我对模板概念很陌生,例如SFINAEortag dispatching并且一直在阅读一些关于它的文章和示例,这些文章和示例对我的方法没有帮助。因此,如果有人可以提供帮助,我将不胜感激。

我的目标是拥有 1 个单一的解析函数,它会在将数据传递给其他一些函数以根据模板T类型进行特定解析之前做一些事情。
在附加的代码中,这是我想要的一种行为。我在这里使用if constexpr的是一个 C++17 功能,在我使用的 C++ 版本中不可用。
我认为为此目的,乍一看似乎template specialization是最好的解决方案,但这不是我想要的。
我认为为此目的tag dispatching将是一个很好的方向,但我不确定type_traits当我有自定义类型时如何准确地做到这一点,因为它总是像我有 2 个选项 a true_typeor 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;
}
4

2 回答 2

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);
}
于 2018-10-23T11:59:54.517 回答
1

为什么不为 提供模板特化parseFooparseBar然后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;
}

如果你不想要额外的模板参数,你可以让用户在Fooand和任何东西中提供一个标签类,但是当你有一个of sBar时将不起作用:vectorFoo

// 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;
}
于 2018-10-23T11:27:02.933 回答