模板化参数是模板的模板化类专门化

Ben*_*ard 41 c++ templates

我想知道是否有类似的东西是可能的.基本上,我有一个模板化的类偶尔会接受模板类的对象.我想专门为它(或只是一个成员函数)的特定模板类,但该类的"通用"形式.

template<typename T, typename S>
class SomeRandomClass
{
    //put something here
};

template<typename T>
class MyTemplateClass
{
    void DoSomething(T & t) {
       //...something
    }
};

template<>
void MyTemplateClass< SomeRandomClass<???> >::DoSomething(SomeRandomClass<???> & t)
{
    //something specialized happens here
}
Run Code Online (Sandbox Code Playgroud)

用适当的类型(double等)替换问号是有效的,但我希望它保持通用.我不知道该放什么,因为任何类型都没有定义.我环顾四周,了解了模板模板参数,尝试了各种组合无济于事.谢谢您的帮助!

Rya*_*oun 24

可以像这样专门化这个类

template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
    void DoSomething(SomeRandomClass<T,S>& t) { /* something */ }
};
Run Code Online (Sandbox Code Playgroud)

仅仅专门化成员方法是不可能的,因为专门化作为一个整体在类上,你必须定义一个新类.但是,你可以这样做

template <>
template <typename T,typename S>
class MyTemplateClass <SomeRandomClass<T,S> >
{
    void DoSomething(SomeRandomClass<T,S>& t);
};

template <>
template <typename T,typename S>
void MyTemplateClass<SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S>& t)
{
    // something
}
Run Code Online (Sandbox Code Playgroud)

分割声明和定义.


And*_*dyG 21

我不完全确定为什么@Ryan Calhoun 专注于他的方式,但这是一个更简洁的例子:

// class we want to specialize with later on
template<typename T, typename S>
struct SomeRandomClass
{
    int myInt = 0;
};

// non-specialized class
template<typename T>
struct MyTemplateClass
{
    void DoSomething(T & t) 
    {
       std::cout << "Not specialized" << std::endl;
    }
};

// specialized class
template<typename T, typename S>
struct MyTemplateClass< SomeRandomClass<T, S> >
{
    void DoSomething(SomeRandomClass<T,S> & t) 
    {
       std::cout << "Specialized" << std::endl;
    }
};
Run Code Online (Sandbox Code Playgroud)

您可以看到您不需要在接受的答案中使用冗余语法:

template<>
template<typename T, typename S>
Run Code Online (Sandbox Code Playgroud)

工作演示


替代

您可以在非专业类中使用type_traits和tag-dispatch来专门化该函数.

让我们首先提出一个概念is_random_class:

// concept to test for whether some type is SomeRandomClass<T,S>
template<typename T>
struct is_random_class : std::false_type{};

template<typename T, typename S>
struct is_random_class<SomeRandomClass<T,S>> : std::true_type{};
Run Code Online (Sandbox Code Playgroud)

然后让我们MyTemplateClass再次声明我们,但这次没有模板化(因为我们没有专门化)所以我们称之为MyNonTemplatedClass:

class MyNonTemplatedClass
{

    public:
    template<typename T>
    void DoSomething(T & t) 
    {
       DoSomethingHelper(t, typename is_random_class<T>::type());
    }
    // ...
Run Code Online (Sandbox Code Playgroud)

注意DoSomething现在是如何模板化的,它实际上是在调用助手而不是实现逻辑本身?

让我们分解一下:

DoSomethingHelper(t, typename is_random_class<T>::type());
Run Code Online (Sandbox Code Playgroud)
  • t是以前的; 我们正在传递类型的论点T&
  • typename is_random_class<T>::type()
    • is_random_class<T>是我们的概念,因为它源于std::true_type或者std::false_type它将::type在类中定义(谷歌的"类型特征")
    • ::type()'实例化'指定的类型is_random_class<T>::type.我用引号说出来,因为我们真的会把它扔掉,就像我们后面看到的那样
    • typename是必需的,因为编译器不知道is_random_clas<T>::type实际命名类型.

现在我们准备好了解其余部分MyNonTemplatedClass:

    private:
    //use tag dispatch. If the compiler is smart it won't actually try to instantiate the second param
    template<typename T>
    void DoSomethingHelper(T&t, std::true_type)
    {
        std::cout << "Called DoSomething with SomeRandomClass whose myInt member has value " << t.myInt << std::endl;
    }

    template<typename T>
    void DoSomethingHelper(T&t, std::false_type)
    {
        std::cout << "Called DoSomething with a type that is not SomeRandomClass\n";
    }
};
Run Code Online (Sandbox Code Playgroud)

完整的工作演示v2在这里

请注意,我们的辅助函数名称相同,但在第二个参数的类型上重载.我们没有为参数命名,因为我们不需要它,并且希望编译器在仍然调用正确函数的同时优化它.

我们的概念强制DoSomethingHelper(T&t, std::true_type)只有T类型SomeRandomClass,并调用另一种类型.

标签发送的好处

标签分派的主要好处是,如果您只想专门化该类中的单个函数,则不需要专门化整个类.

标记调度将在编译时发生,如果您尝试仅在DoSomething函数内对概念执行分支,则无法获得.

  • 如果在2010年你希望你的代码在rhel4和rhel5上使用gcc进行编译,并且在windows xp上使用visual studio 2008进行编译,那么这就是你编写它的方式. (3认同)

Jas*_*son 10

您需要做的只是模拟您想要保持通用的内容.以你的开始:

template<typename T, typename S>
void MyTemplateClass< SomeRandomClass<T,S> >::DoSomething(SomeRandomClass<T,S> & t)
{
    //something specialized happens here
}
Run Code Online (Sandbox Code Playgroud)

编辑:

或者,如果您只想保留SomeRandomClass通用的一部分,您可以:

template<typename T>
void MyTemplateClass< SomeRandomClass<T,int> >::DoSomething(SomeRandomClass<T,int> & t)
{
    //something specialized happens here
}
Run Code Online (Sandbox Code Playgroud)

  • 那些我认为差不多了,但我无法编译(使用g ++或intel).我认为将模板<typename T>放在那里会使编译器混淆不认识为专业化(即需要一个模板<>,对吗?) (2认同)