给出Foo类
template <typename T>
class Foo
{
public:
...other methods..
void bar()
{
...
m_impl.doSomething();
...
}
void fun()
{
...
m_impl.doSomethingElse();
...
}
void fubar()
{
...
}
private:
T m_impl;
};
我想迎合T是boost :: shared_ptr的情况。 在这种情况下,对Foo类的唯一更改是它应该调用
m_impl->doSomething();
而不是
m_impl.doSomething();
我最终在相同的标题中定义了FooPtr
template <typename T>
class FooPtr
{
public:
...other methods..
void bar()
{
...
m_pImpl->doSomething();
...
}
void fun()
{
...
m_pImpl->doSomethingElse();
...
}
void fubar()
{
...
}
private:
boost::shared_ptr<T> m_pImpl;
};
现在虽然该方法适用于我想要与Foo一起使用的所有类, 问题是我有很多重复的代码和任何变化 我向Foo做,我也要做到FooPtr。
如何重构代码?例如。如果T是boost :: shared_ptr类型,有什么方法可以在编译时确定,然后专门调用bar和fun方法来调用 - &gt;操作
编辑: 谢谢你到目前为止的所有答案!我只需要一些时间来完成它们,看看哪种解决方案最适合我们的软件。
编辑2: @Matthieu:这是我使用的测试代码
class FooImpl
{
public:
void doIt()
{
cout << "A" << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
Foo<FooImpl> foo;
foo.doSomething();
return 0;
}
答案 0 :(得分:4)
Sylvain写了一个DRY解决方案,但我不喜欢滥用继承。
使用包装器类来简化接口很容易,特别是因为指针语义工作得很好!
namespace details {
template <typename T>
struct FooDeducer {
typedef boost::optional<T> type;
};
template <typename T>
struct FooDeducer< T* > {
typedef T* type;
};
template <typename T>
struct FooDeducer< boost::shared_ptr<T> > {
typedef boost::shared_ptr<T> type;
};
} // namespace details
template <typename T>
class Foo {
public:
// methods
void doSomething() { impl->doIt(); }
private:
typedef typename details::FooDeducer<T>::type Type;
Type impl;
};
在这里,依靠提供OptionalPointee语义的boost::optional
,我们几乎得到与指针相同的行为。
我想强调的一点是复制行为的差异。 boost::optional
提供深层复制。
答案 1 :(得分:2)
class A
{
public:
void doSomething() {}
};
template <typename T>
class Foo
{
public:
void bar()
{
Impl(m_impl).doSomething();
}
private:
template<typename P>
P& Impl(P* e)
{
return *e;
}
template<typename P>
P& Impl(std::shared_ptr<P> e)
{
return *e;
}
template<typename P>
P& Impl(P& e)
{
return e;
}
T m_impl;
};
答案 2 :(得分:2)
您可以根据caller
的类型编写obj.f()
类模板,其作用是使用语法obj->f()
或obj
调用该函数。< / p>
这是一个展示这种方法的小例子:
template<typename T>
struct caller
{
static void call(T &obj) { obj.f(); } //uses obj.f() syntax
};
template<typename T>
struct caller<T*>
{
static void call(T* obj) { obj->f(); } //uses obj->f() syntax
};
此示例类使用此caller
类模板:
template<typename T>
struct X
{
T obj;
X(T o) : obj(o) {}
void h()
{
caller<T>::call(obj); //this selects the appropriate syntax!
}
};
在ideone上查看此在线运行演示:http://www.ideone.com/H18n7
-
编辑:
这更通用。在这里,您甚至可以在caller
中传递您要呼叫的功能。现在caller
没有使用要调用的函数进行硬编码!
答案 3 :(得分:1)
您可以引入另一个中间模板类,类似于:
template < typename T >
class FooBase
{
private:
T m_impl;
protected:
T& impl() { return m_impl; }
};
template < typename T >
class FooBase< boost::shared_ptr< T > >
{
private:
boost::shared_ptr< T > m_impl;
protected:
T& impl() { return *(m_impl.operator ->()); }
};
template < typename T >
class Foo : protected FooBase< T >
{
public:
void bar()
{
impl().DoSomething();
}
};
现在,您只需要对Foo
类进行一次编码。并且您可以通过对FooBase
进行部分特化来专门化其他智能指针类型。
编辑:您也可以使用合成而不是Foo
和FooBase
之间的继承关系(在这种情况下,我可能会将其重命名为FooHelper
或类似的东西)
template < typename T >
class FooHelper
{
private:
T m_impl;
public:
T& impl() { return m_impl; }
};
template < typename T >
class FooHelper< boost::shared_ptr< T > >
{
private:
boost::shared_ptr< T > m_impl;
public:
T& impl() { return *(m_impl.operator ->()); }
};
template < typename T >
class Foo
{
private:
FooHelper< T > m_helper;
public:
void bar()
{
m_helper.impl().DoSomething();
}
};
答案 4 :(得分:1)
我真的怀疑你是否应该在这里使用模板。您的模板参数具有非常清晰的界面,因此您应该只使用抽象基类。
你真的需要一个实例吗?如果确实需要更改对象的表示方式,则应将其作为单独的练习完成,而不是使用它的模板的一部分。
答案 5 :(得分:0)
您可以使用部分专业化。
template <typename T>
class Foo
{
public:
//...
};
template<typename T> class Foo<boost::shared_ptr<T>> {
//... implement specialization here
};