【问题标题】:Function specialization in template definition with template template parameters具有模板模板参数的模板定义中的函数特化
【发布时间】:2017-08-22 10:53:58
【问题描述】:

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

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 附加到 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);
}

【问题讨论】:

  • 如果我没记错的话,你不能将类模板中的函数与类本身分开来专门化
  • 这是一个典型的 XY 问题。您上一个问题的正确答案是 Toby Speight。这里不需要这种复杂的结构。
  • 错误 Word 未定义。
  • 对不起,我在 cpp.sh 上创建了一个脚本:cpp.sh/4pn4z
  • 仅供参考,变量名__xreserved identifier,不要使用它。

标签: c++ templates stl


【解决方案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

【讨论】:

  • 第一种方法对我来说很好。第二个远远超出了我的知识和要求,但无论如何,它让我大开眼界。
【解决方案2】:

您可以使用 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;
}

【讨论】:

  • 我确信这种方法也有效,但来自@AndyG 的方法正是我想要的。
  • @HolaMund > 这正是我的回答所做的。
  • 你说得对,这是我出于好奇而添加的一个要求,但实际上我并不需要它。
  • @HolaMundo 好吧,那部分似乎解决起来最有趣:-)
猜你喜欢
  • 2018-06-25
  • 2015-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
相关资源
最近更新 更多