模板类专门化,其中模板参数是模板

时间:2010-11-15 23:41:19

标签: 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
}

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

5 个答案:

答案 0 :(得分:21)

可以像这样专门化这个课程

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

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

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
}

分割声明和定义。

答案 1 :(得分:15)

我不完全确定为什么@Ryan Calhoun specialized the way he did但是这里有一个更简洁的例子:

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

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

template<>
template<typename T, typename S>

Working Demo

替代

您可以在非专业类中使用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{};

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

MyNonTemplatedClass

注意class MyNonTemplatedClass { public: template<typename T> void DoSomething(T & t) { DoSomethingHelper(t, typename is_random_class<T>::type()); } // ... 现在是如何模仿的,它实际上是在调用辅助函数而不是实现逻辑本身?

让我们打破界限:

DoSomething
  • DoSomethingHelper(t, typename is_random_class<T>::type()); 和以前一样;我们传递了t
  • 类型的参数
  • T&
    • typename is_random_class<T>::type()是我们的概念,因为它来自is_random_class<T>std::true_type,它会在课程中定义std::false_type(Google for&#34; type traits& #34)
    • ::type&#39;实例化&#39; ::type()指定的类型。我用引号说出来,因为我们真的会把它扔掉,就像我们后面看到的那样
    • is_random_class<T>::type是必需的,因为编译器不知道typename实际上是一个类型的名称。

现在我们已准备好查看其余is_random_clas<T>::type

MyNonTemplatedClass

Full Working Demo v2 Here

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

我们的概念仅在 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"; } }; 类型为DoSomethingHelper(T&t, std::true_type)时强制T,并将其他类型调用为任何其他类型。

标签发送的好处

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

标签调度将在编译时进行,如果您尝试仅在SomeRandomClass函数内对该概念执行分支,则无法获得。

答案 2 :(得分:10)

您需要做的只是关于您想要保持通用的模板。以你的开始:

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

编辑:

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

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

答案 3 :(得分:0)

编辑:这是对其他问题的正确答案。

使用typename T两次混淆问题,因为它们是单独编译的,并且没有以任何方式连接。

您可以重载方法以获取模板化参数:

template <typename T>
class MyTemplateClass
{
    void DoSomething(T& t) { }

    template <typename U,typename V>
    void DoSomething(SomeRandomClass<<U,V>& r) { }
};

这会将新方法中的UV映射到T'中的S'SomeRandomClass。在此设置中,UV可以与T的类型相同,但不一定是MyTemplateClass<string> mine; SomeRandomClass<int,double> random; // note: nevermind the non-const ref on the string literal here... mine.DoSomething("hello world"); mine.DoSomething(random); 。根据您的编译器,您应该能够

DoSomething

并且模板化调用将被选为匹配重载,而无需明确重新指定类型。

修改

使用模板专精化对template <> class SomeRandomClass <int,double> { // something here... }; 的重载没有影响。如果您将课程专门化如下

DoSomething

然后上面的重载会很高兴地吃掉这个专门的实现。只需确保专用模板和默认模板的接口匹配。

如果您想要的是SomeRandomClass专门为{{1}}选择一对特定的类型,那么您已经失去了普遍性......那就是专业化。

答案 4 :(得分:0)

如果您想使用提供模板结构作为模板参数(意图在内部使用它)而不对其进行专门化:

下面是一个例子,在给定模板 sfinae 结构作为模板参数的情况下,将类型附加到元组:

template<typename Tuple, typename T, template<typename> class /*SFINAEPredicate*/>
        struct append_if;

        template<typename T, template<typename> class SFINAEPredicate, typename ... Types>
        struct append_if<std::tuple<Types...>, T, SFINAEPredicate>
        {
            using type = typename std::conditional<SFINAEPredicate<T>::value,
                    std::tuple<Types..., T>, std::tuple<Types...>>::type;
        };


    // usage
    using tuple_with_int = append_if<std::tuple<>, int, std::is_fundamental>;

这可以从 C++11 开始使用。