具有可变模板模板参数的方法的部分特化

时间:2017-06-22 21:59:18

标签: c++ c++11 c++14

我有一个成功编译的代码(g ++ 4.9.2):

#include <iostream>
#include <utility>

// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void
foo(T<TTs...>& arg)
{
    std::cout << "T<TTs...>" << std::endl;
}

template<typename ...Ts>
struct xxx
{
    // not important
};

// specialization for only variadic templated xxx
template<typename ...TTs> void
foo(xxx<TTs...>& arg)
{
    std::cout << "xxx<TTs...>" << std::endl;
}

// specialization for non-variadic templated xxx
template<typename TT> void
foo(xxx<TT>& arg)
{
    std::cout << "xxx<TT>" << std::endl;
}

// specialization for xxx<uint8_t>
template<> void
foo(xxx<uint8_t>& arg)
{
    std::cout << "xxx<uint8_t>" << std::endl;
}

int
main(int argc, char** argv)
{
    xxx<uint8_t> x1;
    std::cout << "xxx<uint8_t> => ";
    foo(x1);

    xxx<uint16_t> x2;
    std::cout << "xxx<uint16_t> => ";
    foo(x2);

    xxx<uint8_t,uint16_t> x3;
    std::cout << "xxx<uint8_t,uint16_t> => ";
    foo(x3);

    std::pair<uint8_t,uint16_t> x4;
    std::cout << "std::pair<uint8_t,uint16_t> => ";
    foo(x4);

    return 0;
}

并产生:

xxx<uint8_t> => xxx<uint8_t>
xxx<uint16_t> => xxx<TT>
xxx<uint8_t,uint16_t> => xxx<TTs...>
std::pair<uint8_t,uint16_t> => T<TTs...>

现在我想在一个类中使用这些foo方法,并编写:

#include <iostream>
#include <utility>

class abc
{
public:
    // general function for any variadic templated argument
    template<template<typename ...> class T, typename ...TTs>
    void
    foo(T<TTs...>& arg)
    {
        std::cout << "T<TTs...>" << std::endl;
    }
};

template<typename ...Ts>
struct xxx
{
    // not important
};

// specialization for only variadic templated xxx
template<typename ...TTs> void
abc::foo(xxx<TTs...>& arg)
{
    std::cout << "xxx<TTs...>" << std::endl;
}

// specialization for non-variadic templated xxx
template<typename TT> void
abc::foo(xxx<TT>& arg)
{
    std::cout << "xxx<TT>" << std::endl;
}

// specialization for xxx<uint8_t>
template<> void
abc::foo(xxx<uint8_t>& arg)
{
    std::cout << "xxx<uint8_t>" << std::endl;
}

int
main(int argc, char** argv)
{
    abc p;

    xxx<uint8_t> x1;
    std::cout << "xxx<uint8_t> => ";
    p.foo(x1);

    xxx<uint16_t> x2;
    std::cout << "xxx<uint16_t> => ";
    p.foo(x2);

    xxx<uint8_t,uint16_t> x3;
    std::cout << "xxx<uint8_t,uint16_t> => ";
    p.foo(x3);

    std::pair<uint8_t,uint16_t> x4;
    std::cout << "std::pair<uint8_t,uint16_t> => ";
    p.foo(x4);

    return 0;
}

,这会产生编译错误:

test_ko.cc:24:1: error: prototype for ‘void abc::foo(xxx<TTs ...>&)’ does not match any in class ‘abc’
 abc::foo(xxx<TTs...>& arg)
 ^
test_ko.cc:10:5: error: candidate is: template<template<class ...> class T, class ... TTs> void abc::foo(T<TTs ...>&)
     foo(T<TTs...>& arg)
     ^
test_ko.cc:31:1: error: prototype for ‘void abc::foo(xxx<TT>&)’ does not match any in class ‘abc’
 abc::foo(xxx<TT>& arg)
 ^
test_ko.cc:10:5: error: candidate is: template<template<class ...> class T, class ... TTs> void abc::foo(T<TTs ...>&)
     foo(T<TTs...>& arg)

我想要一个foo方法的特化,而不是在abc类中声明一个新的签名(因为有一个类包含模板,一个库的一部分,以及单独的特化,对于后来声明的xxx类)。

我认为使用方法的代码与具有函数的代码类似,但我在这里得到错误。我做错了什么?

1 个答案:

答案 0 :(得分:1)

// general function for any variadic templated argument
template<template<typename ...> class T, typename ...TTs>
void
foo(T<TTs...>& arg)
{
  std::cout << "T<TTs...>" << std::endl;
}

这是一个模板功能。

template<typename ...Ts>
struct xxx
{
  // not important
};

// specialization for only variadic templated xxx
template<typename ...TTs> void
foo(xxx<TTs...>& arg)
{
  std::cout << "xxx<TTs...>" << std::endl;
}

专业化。这是一个不同的模板函数,其名称foo与上述模板函数重载。

// specialization for non-variadic templated xxx
template<typename TT> void
foo(xxx<TT>& arg)
{
  std::cout << "xxx<TT>" << std::endl;
}

专业化。这是一个不同的模板函数,其名称foo与上述模板函数重载。

// specialization for xxx<uint8_t>
template<> void
foo(xxx<uint8_t>& arg)
{
  std::cout << "xxx<uint8_t>" << std::endl;
}

这是上述模板函数之一的完全特化。我认为第三个,但我不会赌钱。 (我相信如果您调用xxx<uint8_t>&),它会专门用于调度foo模板。

而不是完全专业化,我会写:

inline foo(xxx<uint8_t>& arg)
{
  std::cout << "xxx<uint8_t>" << std::endl;
}

这又是foo的全新重载。重载远不如完整功能专业化那么古怪。

没有部分模板功能专业化

这解释了为什么尝试使用相同语法专门化方法不起作用。 也没有部分模板成员功能专业化这样的东西。

您必须在类本身中编写重载,或者调度到不同的上下文。

出错的原因是错误的直接原因是您的初始代码引入了新的重载。不允许在类定义之外引入方法的新重载,因此编译器指出了您的错误。

这是一种有用的技术。我们在abc

// general function for any variadic templated argument
  template<template<typename ...> class T, typename ...TTs>
  void foo(T<TTs...>& arg)
  {
    return foo(*this, arg);
  }
private:
  template<template<typename ...> class T, typename ...TTs>
  friend void foo(abc& self, T<TTs...>& arg)
  {
    std::cout << "T<TTs...>" << std::endl;
  }

我们的foo方法扩展为foo朋友。

然后我们在与abc相同的命名空间中添加代码:

template<typename ...TTs> void
foo(abc& self, xxx<TTs...>& arg)
{
  std::cout << "xxx<TTs...>" << std::endl;
}

template<typename TT> void
foo(abc& self, xxx<TT>& arg)
{
  std::cout << "xxx<TT>" << std::endl;
}

inline void foo(abc& self, xxx<uint8_t>& arg)
{
  std::cout << "xxx<uint8_t>" << std::endl;
}

并且在调用abc::foo时通过ADL找到它们。