我想知道是否有类似的东西是可能的。基本上,我有一个模板化的类偶尔会接受模板类的对象。我想专门为它(或只是一个成员函数)的特定模板类,但该类的“通用”形式。
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等)替换问号有效,但我希望它保持通用。我不知道该放什么,因为任何类型都没有定义。我环顾四周,了解了模板模板参数,尝试了各种组合无济于事。谢谢你的帮助!
答案 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>
您可以在非专业类中使用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
请注意,我们的辅助函数名称相同,但在第二个参数的类型上重载。我们没有给参数命名,因为我们不需要它,希望编译器在调用正确函数的同时优化它。
我们的概念仅在 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) { }
};
这会将新方法中的U
和V
映射到T'
中的S'
和SomeRandomClass
。在此设置中,U
或V
可以与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 开始使用。