现在我有一个模板类
template <class T>
class b
{
void someFunc() {
T t;
t.setB();
}
};
我知道模板T只会被实例化为2个类。
class D
{
public:
void setB();
};
class R
{
public:
void SetB();
};
我们可以看到,D类的功能名称 setB 与R的功能 SetB 不同。所以在模板类b中我不仅可以使用setB。如果我不能修改D或R,是否有一些方法?我可以在模板类中添加一些包装器或技巧来解决这个问题吗?
答案 0 :(得分:3)
也许特质课可以帮助你:
struct Lower {};
struct Upper {};
// trait for most cases
template <typename T>
struct the_trait {
typedef Lower Type;
};
// trait for special cases
template <>
struct the_trait<R> {
typedef Upper Type;
};
template <class T>
class b {
public:
void foo() {
foo_dispatch(typename the_trait<T>::Type());
}
private:
void foo_dispatch(Lower) {
T t;
t.setB();
}
void foo_dispatch(Upper) {
T t;
t.SetB();
}
};
正如@Arunmu所指出的,这种技术也被称为Tag Dispatching。
答案 1 :(得分:2)
您可以为具有不同语义的类专门化您的模板:
template<>
class b<R>
{
void doWork() {
R obj;
obj.SetB();
// or R::SetB() if it was a static method.
}
};
答案 2 :(得分:0)
您也可以使用SFINAE检查是否存在自我编程的特征,而不是使用自编程特征。
如果要切换被调用方法,则每个类中只能存在其中一个方法。如果检查发现多于一种经过测试的方法,我提供的方法将无效!
以下示例是为C ++ 14编写的,但如果用自己实现的库函数替换新的库函数,也可以与c ++ 03一起使用(这当然不方便)
测试类has_Foo和has_Bar也可以嵌入到预处理器宏中,但我将其编写为扩展以使事情更容易阅读。
注释中解释了它的工作原理以及为什么需要更多中间步骤。见下文!
#include <iostream>
// First we write two classes as example. Both classes represents
// external code which you could NOT modify, so you need an
// adapter to use it from your code.
class A
{
public:
void Foo() { std::cout << "A::Foo" << std::endl; }
};
class B
{
public:
void Bar() { std::cout << "B::Bar" << std::endl; }
};
// To benefit from SFINAE we need two helper classes which provide
// a simple test functionality. The solution is quite easy...
// we try to get the return value of the function we search for and
// create a pointer from it and set it to default value nullptr.
// if this works the overloaded method `test` returns the data type
// one. If the first test function will not fit, we cat with ... all
// other parameters which results in getting data type two.
// After that we can setup an enum which evaluates `value` to
// boolean true or false regarding to the comparison function.
template <typename T>
class has_Foo
{
using one = char;
using two = struct { char a; char b;};
template <typename C> static one test( typename std::remove_reference<decltype(std::declval<C>().Foo())>::type* );
template <typename C> static two test( ... ) ;
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
enum { Yes = sizeof(test<T>(0)) == sizeof(one) };
enum { No = !Yes };
};
template <typename T>
class has_Bar
{
using one = char;
using two = struct { char a; char b;};
template <typename C> static one test( typename std::remove_reference<decltype(std::declval<C>().Bar())>::type* );
template <typename C> static two test( ... ) ;
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
enum { Yes = sizeof(test<T>(0)) == sizeof(one) };
enum { No = !Yes };
};
// Now in your adapter class you can use the test functions
// to find out which function exists. If your class
// contains a Foo function the first one compiles and if the
// the class contains a Bar function the second one fits. SFINAE
// disable the rest.
// We need a call helper here because SFINAE only
// fails "soft" if the template parameter can deduced from the
// given parameters to the call itself. So the method
// Call forwards the type to test "T" to the helper method as
// as explicit parameter. Thats it!
template <typename T>
class X: public T
{
public:
template < typename N, std::enable_if_t< has_Foo<N>::value>* = nullptr>
void Call_Helper() { this->Foo(); }
template < typename N, std::enable_if_t< has_Bar<N>::value>* = nullptr>
void Call_Helper() { this->Bar(); }
void Call() { Call_Helper<T>(); }
};
int main()
{
X<A> xa;
X<B> xb;
xa.Call();
xb.Call();
}