use*_*168 7 c++ templates explicit-specialization
嗨,我在选择具有明确特化的模板化类的正确版本时遇到问题.我想要使用用于专门化的类的派生类来选择特化.场景是:
#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() );
}
Run Code Online (Sandbox Code Playgroud)
正如我在评论中所说的那样,我希望看到20被打印出来,因为B是从A派生出来的,而10则是打印出来的.如何在不诉诸为每个派生类编写专门化的情况下调用专门化(我的实际场景有很多派生类型).
---编辑:新的答案让我们让原始方法更易于维护.所有重要的选择都可以在Foo的定义中找到.它应该易于维护.
#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;
}
Run Code Online (Sandbox Code Playgroud)
---原始答案如果您可以使用boost,您可以执行以下操作:
#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>(); }
};
Run Code Online (Sandbox Code Playgroud)
更一般地说,一般来说,模板和继承是一个长期存在的问题.
问题是模板在确切类型上工作并且不考虑继承因子,这两个概念在某种程度上是正交的,因此尝试混合一个和另一个通常容易出错.
你也可以用方法检查出来:
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)
Run Code Online (Sandbox Code Playgroud)
现在,关于你的问题,虽然我很欣赏使用enable_if和is_base_of欺骗的各种解决方案,但我认为它们不实用.专业化的一点是作者Foo不必知道如果有必要,任何人都会专门研究她的课程,只是为了让它变得简单.否则,如果你需要十几个专业,你最终会得到一个非常奇怪的Foo课程,这是肯定的.
STL已经处理过类似的问题.接受的习语通常是提供特质课程.默认traits类为每个人提供了一个很好的解决方案,同时可以专门设置一个traits类来满足一个人的需要.
我认为应该有一种方法使用Concepts(即,如果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); }
};
Run Code Online (Sandbox Code Playgroud)
而现在,专门为A的派生类:
namespace detail { int fooBar(A*) { return 20; } }
Run Code Online (Sandbox Code Playgroud)
它是如何工作的?在考虑重载时,省略号是考虑的最后一种方法,因此任何符合条件的方法都可以,因此它非常适合默认行为.
一些考虑:
命名空间:取决于标识符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();
}
}
Run Code Online (Sandbox Code Playgroud)
在这里会发生什么:
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;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
851 次 |
| 最近记录: |