具有模板模板参数的模板定义中的函数专业化

时间:2017-08-22 10:53:59

标签: c++ templates stl

根据previous question中@Jonas的建议,我定义了一个模板化的类来保存任意容器(字符串,树等)的任意容器(向量,集合,映射等)。到目前为止,我的定义是这样的:

template <template <typename...> class Container, typename Containee = std::string, typename... extras>
class Lemario
{
public:
    typedef typename Container<Containee, extras...>::iterator iterator;
    typedef typename Container<Containee, extras...>::const_iterator const_iterator;
    // Use 'Containee' here (if needed) like sizeof(Containee)
    // or have another member variable like: Containee& my_ref.
    Container<Containee, extras ...> mTheContainer;
    int loadContainees(const char *filename) {
        Containee w, line;
        // do some stuff here
    }
    void appendContainee(const Containee &__x);
};

现在,我可以在模板定义的外部定义内联方法(如loadContainees)。外:

template <template <typename...> class Container, typename Containee, typename... extras>
Containee Lemario<Container, Containee, extras...>::transform_word(const     Containee& word) const
{
    Containee result;
    return result;
}

到目前为止一切顺利。

但是现在我想专门化一个方法将Contaniee作为vector,map,tree附加到Container,使用不同的方法。所以我尝试专门化std :: vector:

template <template <typename...> class Container, typename Containee, typename... extras>
void Lemario<std::vector, Word>::appendContainee(const Word & word)
{
     mTheContainer.push_back(word);
}

但是我收到以下错误:

error: prototype for ‘void Lemario<std::vector, gong::Xtring>::appendContainee(const Word&)’ does not match any in class ‘Lemario<std::vector, gong::Xtring>’

除此之外,我可以只对Container,std :: vector进行专门化,但是让Containee不专业吗?

template <template <typename...> class Container, typename Containee, typename... extras>
void Lemario<std::vector, Containee>::appendContainee(const Containee & word)
{
    mTheContainer.push_back(word);
}

2 个答案:

答案 0 :(得分:1)

您可以使用std::enable_if(和SFINAE)来专门化该功能,例如online

template <template <typename...> class Container, typename Containee = std::string, typename... extras>
class Lemario
{
public:
    typedef typename Container<Containee, extras...>::iterator iterator;
    typedef typename Container<Containee, extras...>::const_iterator const_iterator;
    // Use 'Containee' here (if needed) like sizeof(Containee)
    // or have another member variable like: Containee& my_ref.
    Container<Containee, extras ...> mTheContainer;
    int loadContainees(const char *filename) {
        Containee w, line;
        // do some stuff here
        return 0; // To make the compiler happy
    }

    template <typename Containee2> // For SFINAE to work
    typename std::enable_if<std::is_same<Container<Containee2, extras...>, std::vector<Containee2, extras...>>::value>::type
    appendContainee(const Containee2 & word)
    {
         mTheContainer.push_back(word);
    }

    template <typename Containee2> // For SFINAE to work
    typename std::enable_if<std::is_same<Container<Containee2, extras...>, std::deque<Containee2, extras...>>::value>::type
    appendContainee(const Containee2 & word)
    {
         mTheContainer.push_back(word);
    }
};

示例

int main()
{
  Lemario<std::vector, int> foo;
  std::cout << foo.mTheContainer.size() << std::endl;
  foo.appendContainee(1);
  std::cout << foo.mTheContainer.size() << std::endl;

  Lemario<std::deque, int> bar;
  std::cout << bar.mTheContainer.size() << std::endl;
  bar.appendContainee(1);
  std::cout << bar.mTheContainer.size() << std::endl;
}

答案 1 :(得分:1)

问题是您的appendContainee实现存在语法错误。专门针对std::vectorWord的函数的正确方法是这样写:

template <>
void Lemario<std::vector, Word>::appendContainee(const Word & word)
{
     mTheContainer.push_back(word);
}

Demo

但是,这种方法要求您每次都完全专门化该函数,这意味着您必须同时指定容器类型。可能你只想专注于std::vector而不是Word

这个问题通过 tagged-dispatch 模式来解决。在标记的调度中,我们通过引入将通过重载正确选择的辅助方法将专门化转变为重载问题。

我们在类中创建一个空的嵌套模板化结构:

private:
   template<class...>
   struct Lemario_tag{};

然后编写入口点方法将调用的private辅助方法:

template<class T>
void appendContaineeHelp(const Containee &x, Lemario_tag<T>)
{
    static_assert(sizeof(T) == 0, "No specialization exists for this container");
}

void appendContaineeHelp(const Containee &x, Lemario_tag<std::vector<Containee, extras...>>)
{
    mTheContainer.push_back(x);
}

如果你尝试使用非专用容器调用它,那么第一个是全能的,会导致编译错误。

第二个专门用于std::vector

我们定义public appendContainee就像这样(简单传递):

void appendContainee(const Containee &x){
    appendContaineeHelp(x, Lemario_tag<Container<Containee, extras...>>{});
}

我们可以像这样使用我们的容器:

Lemario<std::vector, std::string> vecString;
Lemario<std::vector, int> vecInt;
vecString.appendContainee("foo");
vecInt.appendContainee(1);

Lemario<std::set, int> set_int;
// set_int.appendContainee(1); // compiler error

Better Demo