将C ++模板“ if constexpr”转换为旧的“标记分派”方法

时间:2018-10-23 10:21:35

标签: c++ templates c++17 tag-dispatching

我对SFINAEtag dispatching之类的模板概念非常陌生,并一直在阅读一些文章和示例,这些文章和示例对我的方法没有帮助。因此,如果有人可以帮助我,我将不胜感激。

我的目标是拥有1个单个解析函数,该函数将在将数据转发给其他函数以根据模板T类型进行特定解析之前进行一些处理。
在所附的代码中,这是我想要的行为。我在这里使用if constexpr,不幸的是,这是C ++ 17功能,在我使用的C ++版本中不可用。
我认为,出于这个目的,template specialization看起来是最好的解决方案,但这不是我想要的。
我认为,tag dispatching会是一个很好的方向,但是我不确定在使用自定义类型时如何使用type_traits来实现这一点,因为总是有2个选项,{ {1}}或true_type,但是在下面的代码中,我遇到了3种情况,可能会有更多的情况。

对于某些示例或指导,我将不胜感激,这是执行我所寻找的最佳方法。即使读一些文章也很棒。

谢谢!

工作代码示例:

false_type

2 个答案:

答案 0 :(得分:3)

标签分配将使此处变得更容易:

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);
}

答案 1 :(得分:2)

为什么不只为parseFooparseBarparseFoos提供模板专业化,然后只从静态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)。无论如何,如果您想按照以下代码的方式使用标签分发功能(同样,由于parser函数的另一个模板参数,IMO不太好):

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;
}

如果您不需要额外的模板参数,则可以让用户在FooBar之内以及其他任何地方提供标记类,但是当您拥有{{1 }}个vector

Foo

另一个编辑: 您可以为Tag创建一个模板类,以便摆脱// 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