选择一个明确的专业化的一类基于源类型
-
22-09-2019 - |
题
嗨,我是有问题的选择的正确版本的模板类有一个明确的专业化。我想要选择一个专业化的采用衍生出类的类使用的专门研究。该方案是:
#include <stdio.h>
class A
{};
class B: public A
{};
template<typename T>
class Foo
{
public:
int FooBar(void) { return 10; }
};
// Explicit specialization for A
template<> int Foo< A >::FooBar( void ) { return 20; }
void main( void)
{
Foo<B> fooB;
// This prints out 10 instead of wanted 20 ie compiler selects the general version
printf("%d", fooB.FooBar() );
}
正如我说的在我的意见我想看到20被打印出来,因为B源自但10得到印刷出来代替。我怎么去得到的专业化被称为 没有 诉诸写入一个专业化,为每一个源类别(我的实际情况 很多 的源类型)。
解决方案
---编辑:新的答案让我们把原来的做法更容易维护。 所有重要的选择可以在美孚的定义中找到。它被认为是易于维护。
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_base_of.hpp>
#include <iostream>
class A
{};
class B: public A
{};
class C{};
class D : public C{};
class E{};
struct DefaultMethod
{
static int fooBar() { return 10; }
};
struct Method1
{
static int fooBar() { return 20; }
};
struct Method2
{
static int fooBar() { return 30; }
};
template<typename T, typename BaseClass, typename Choice1, typename OtherChoice>
struct IfDerivesFrom :
boost::mpl::if_<
typename boost::is_base_of<BaseClass, T>::type,
Choice1,
OtherChoice>::type
{
};
template<typename T>
struct Foo :
IfDerivesFrom<T, A,
Method1,
IfDerivesFrom<T, C,
Method2,
DefaultMethod>
>
{
};
int main()
{
std::cout << Foo<A>::fooBar() << std::endl;
std::cout << Foo<B>::fooBar() << std::endl;
std::cout << Foo<C>::fooBar() << std::endl;
std::cout << Foo<D>::fooBar() << std::endl;
std::cout << Foo<E>::fooBar() << std::endl;
return 0;
}
---原来的答案 如果你可以使用提升,你可以做一些这样的:
#include <boost/type_traits/is_base_of.hpp>
template<bool b>
class FooHelper
{
int FooBar();
};
template<> FooHelper<true>::FooBar(){ return 20;}
template<> FooHelper<false>::FooBar(){ return 10;}
template<typename T>
class Foo
{
public:
int FooBar(void) { return FooHelper<boost::is_base_of<A, T>::type::value>(); }
};
其他提示
更一般地说,这是一个长期存在的问题与模板和继承的一般。
问题是,模板工作的确切类型并不考虑继承的因素,这两个概念是有些正交的,因此,试图混合一个和另一个往往容易出错。
你也可以检查它与方法:
template <class T>
int fooBar(T) { return 10; }
int fooBar(A) { return 20; }
B b;
fooBar(b); // this returns 10, because fooBar<T> is a better match (no conversion)
现在,在你的问题,同时我赞赏各种解决方案,已经被赋予使用 enable_if
和 is_base_of
技巧,我放弃他们作为不实际的。点的专业化是,提交人的 Foo
不必知道如何任何人都是专门研究她的班级,如果有必要,仅仅是为了使它容易。否则,如果你需要一个十几个专业,你们结束了一个非常非常奇怪 Foo
级,这是肯定的。
STL已经处理类似的问题。所接受的语通常是提供一个特征类。默认的特征类提供了一个很好的解决方案的每个人都同一个可以专门一个特点类以容纳一个人的需要。
我觉得应该有一种方法使用的概念(即,如果定义T T::fooBar()然后使用它,否则使用缺省版本...),但对于特定的方法载这不是必需的。
namespace detail { int fooBar(...) { return 10; } }
template <class T>
class Foo
{
public:
static int FooBar() { T* t(0); return ::detail::fooBar(t); }
};
而现在,专门针对源类别的一个:
namespace detail { int fooBar(A*) { return 20; } }
它怎么工作的?在考虑重载,省略号是最后一个方法考虑,因此,任何有资格享受之前会这样做,因此它是一个完美的默认行为。
一些考虑:
名字空间:这取决于是否的标识符
fooBar
是可能被使用或不,你可能更喜欢隔离开来到一个名字空间的其自己的(或者专用的Foo
类),否则,让一个不合格打电话,让用户的定义,它在空间她类。这招只适用于继承和方法援引,就不会工作如果你希望在特别typedef
你可以通过更多的模板,以实际的方法,像真正的类型
这里是一个例子模板功能
namespace detail { template <class T> int fooBar(...) { return 10; } }
template <class T>
int Foo<T>::FooBar() { T* t(0); return ::detail::fooBar<T>(t); }
namespace detail {
template <class T>
int fooBar(A*)
{
return T::FooBar();
}
}
和这里会发生什么事:
struct None {};
struct A { static int FooBar() { return 20; } };
struct B: A {};
struct C: A { static int FooBar() { return 30; } };
int main(int argc, char* argv[])
{
std::cout << Foo<None>::FooBar() // prints 10
<< " " << Foo<A>::FooBar() // prints 20
<< " " << Foo<B>::FooBar() // prints 20
<< " " << Foo<C>::FooBar() // prints 30
<< std::endl;
}
首先(次要)点:你的标题是不正确;这是明确的分工,而不是局部的专业化。为了得到局部特殊化,你需要至少指定一个模板参数,但保留至少一个其他未说明的:
template <class T, class U>
demo { };
template <class T>
demo<int> {}; // use in the case of demo<XXX, int>
看你的代码,我有点惊讶的是其编译的。我不知道有什么办法,你可能会迫使你的专门的函数被调用。通常情况下,你会专门类作为一个整体:
template<typename T>
class Foo
{
public:
int FooBar(void) { return 10; }
};
template<>
class Foo<A> {
public:
int FooBar() { return 20; }
};
在这种情况下,将没有真正做你虽然任何好处。可以派生对象转换为基础对象隐含的,但它就是静止的转化率。在另一方面,与模板的未专门版本可以与其一起使用的没有转换 - 当拾取要使用哪一个,所述编译器将一个可与不转换作为一个更好的被实例化选择比要求的隐式转换。
下面是一个解决方案,但它不是特别好,但:
template<typename T>
class Foo
{
public:
int FooBar(typename disable_if<boost::is_base_of<A,T> >::type* dummy = 0) { return 10; }
int FooBar(typename enable_if<boost::is_base_of<A,T> >::type* dummy = 0) { return 20; }
};
您需要专门的确切类型。例如Foo<A> fooA; fooA.FooBar();
会得到你的20
。或使用 boost.type_traits 为@伯努瓦所示。
从根本上说,要对派生类模板专业化触发。
如果您不需要实际的类特殊,只是功能上,它好像你可能只是这样做:
int foo::foobar(A &someA);
如果你这时就需要有真正的类地专业,我想你想看看接口和私有类数据模式;更多或更少的,接口“减少”的对象的模板的专业化识别的类型,然后调用通过;翼
int foo::foobar(A &someA)
{ return fooImpl<A>::foobar(someA); }
但我想这并没有真正回答你的问题,因为它不支持一般的情况下。我想你可以有:
template<class T>
class foo
{
public:
int foobar(T &t);
int foobar(A &a);
}
foo<A>::foobar(someA);
foo<F>::foobar(someA);
foo<not B>::foobar(someB); //Should trigger foobar(A &a), right?
这将随后能够识别乙从A衍生,同时仍然提供的一般情况下。我认为;我还没有测试此。
这不是最漂亮的,但我认为你有一些有趣的访问控制,像有东西一些机会,如果你是专业的实际类,你可以包括或不包括各种foobar的(A&A)类的函数允许或拒绝对各种继承树使用;对于上面的例子;
foo<C>::foobar(someF); //doesn't exist!