C++ 部分类模板专门化是这个设计问题的答案吗?

C++ 部分类模板专门化是这个设计问题的答案吗?,c++,oop,templates,specialization,C++,Oop,Templates,Specialization,假设您有一个类,其工作是连接到远程服务器。我想抽象这个类以提供两个版本,一个通过UDP连接,另一个通过TCP连接。我希望构建尽可能精简的运行时代码,而不是使用多态性,我正在考虑模板。以下是我的设想,但我不确定这是最好的方式: class udp {}; class tcp {}; template<class T,typename X> class service { private: // Make this private so this non specialized v

假设您有一个类,其工作是连接到远程服务器。我想抽象这个类以提供两个版本,一个通过UDP连接,另一个通过TCP连接。我希望构建尽可能精简的运行时代码,而不是使用多态性,我正在考虑模板。以下是我的设想,但我不确定这是最好的方式:

class udp {};
class tcp {};

template<class T,typename X>
class service
{
private:
  // Make this private so this non specialized version can't be used
   service();
};

template<typename X>
class service<udp, X>
{
private:
   udp _udp;
   X _x;
};

template<typename X>
class service<tcp, X>
{
private:
   tcp _tcp;
   X _x;
};
类udp{};
类tcp{};
模板
班级服务
{
私人:
//将此设置为私有,以便无法使用此非专用版本
服务();
};
模板
班级服务
{
私人:
udp udp;
X_X;
};
模板
班级服务
{
私人:
tcp_-tcp;
X_X;
};
因此,最终的好处是T的通用性仍然可用,但设置UDP或TCP连接所需的完全不同的代码已经专门化。我想您可以将这两个类放在一个类中,或者提供另一个类,该类遵循用于设置网络连接的纯虚拟接口,比如IConnectionManager


但这确实留下了通用T的代码问题,现在必须在两个专用版本中编写和维护,它们最终是相同的。如何最好地解决这个问题?我有一种感觉,我完全搞错了。

我会使用奇怪的重现模板模式,即五点手掌爆炸Alexandrescu技术:

template <typename Underlying>
class Transmit
{
public:
   void send(...)
   {
      _U.send(...)
   };

private:
    Underlying _U;
};

class Tcp
{
public:
   void send(...) {};
};

class Udp
{
public:
   void send(...) {};
};
模板
类传输
{
公众:
无效发送(…)
{
_U.send(…)
};
私人:
基础(U);
};
类Tcp
{
公众:
无效发送(…){};
};
类Udp
{
公众:
无效发送(…){};
};
可能会有更多的模板参数和子类,但您也可以使用静态方法


顺便说一句,模板代码通常效率更高,但也更大。

我认为在polimorphism或模板专门化之间进行选择的要点是,至少在这种特殊情况下,如果您想选择在运行时或编译时使用哪种行为。
例如,如果希望基于用户提供的连接字符串建立udp或tcp连接,则polimorphism最适合您的需要;创建一个具体类,然后将其传递给处理指向基接口指针的泛型代码。
否则,您可能会考虑使用模板——我不确定您是否需要模板特化。
希望这有帮助:)

最好使用传输协议的策略:

template<typename Transport>
class service : Transport {
public:
    typedef Transport transport_type;

    // common code
    void do_something() { 
        this->send(....);
    }
};

class tcp {
public:
    void send(....) {

    }
};

class udp {
public:
    void send(....) {

    }
};

typedef service<tcp> service_tcp;
typedef service<udp> service_udp;
模板
班级服务:交通{
公众:
类型def传输类型;
//通用代码
void do_something(){
此->发送(…);
}
};
类tcp{
公众:
无效发送(…){
}
};
类udp{
公众:
无效发送(…){
}
};
类型定义服务\u tcp;
typedef服务_udp;
注意,这也是多态的。这叫做编译时多态性。将策略放入基类将受益于空基类优化。也就是说,基类不需要占用任何空间。将策略作为成员存在另一个缺点,即您总是必须将内容委派给该成员,这可能会随着时间的推移而变得烦人。这本书对这种模式进行了深入的描述

理想情况下,传输协议不需要了解它上面的协议。但是,如果出于某种原因,您必须获得有关它的一些信息,您可以使用crtp模式:

模板
班级服务:交通{
//因为我们是私下衍生的,所以让传输层成为我们的朋友,
//这样它就可以把它的指针指向我们。
朋友级交通工具;
公众:
类型def传输类型;
//通用代码
void do_something(){
此->发送(…);
}
};
模板
类tcp{
公众:
无效发送(…){
}
};
模板
类udp{
公众:
无效发送(…){
}
};
类型定义服务\u tcp;
typedef服务_udp;
您不必将模板放入标题中。如果显式地实例化它们,您将获得更快的编译时间,因为需要包含的代码要少得多。将其投入使用。cpp:

template class service<tcp>;
template class service<udp>;
模板类服务;
模板类服务;

现在,使用服务的代码不需要知道服务的模板代码,因为该代码已经生成到service.cpp的对象文件中

模板不是必需的(尽管是一种可能的解决方案)。这只是通过模板而不是构造函数进行依赖项注入。就我个人而言,我会通过一个构造函数来完成。但是,通过模板来实现它会给您带来一个更便宜的方法调用(它不需要是虚拟的)可疑的好处。但也允许更容易的编译器优化

udp和tcp对象必须仍然支持相同的接口。
如果通过继承实现,则它们必须都实现一个公共接口(虚拟基类),通过模板实现这是不必要的,但编译器将检查它们是否支持服务对象所需的相同方法调用

正如在最初的问题中所问的,我看不到部分模板专门化的明确需要(或好处)(在所描述的情况下)

模板法
有趣的是,这将把实际的特定于协议的代码重新定位到底层,基本上消除了专门化的需要。您的解决方案与CRTP有什么关系?奇怪的重复出现的模板模式表示这样一种情况,即类型使用自身作为模板参数继承模板类:模板类Base{…};派生类:公共基{…};本例中没有CRTP,因为它非常简单,但完整的版本可能是CRTP。如果您愿意,我们可以使用“基于策略的设计”一词。不过,我完全同意您其余的回答。特定于udp或tcp的代码应封装在专用于传输的类中,以便服务
template class service<tcp>;
template class service<udp>;
class udp {/*Interface Plop*/static void plop(Message&);};
class tcp {/*Interface Plop*/static void plop(Message&);};
template<typename T>
class Service
{
    public:
        void doPlop(Message& m) { T::plop(m);}
    // Do not actually need to store an object if you make the methods static.
    // Alternatively:
    public:
        void doPlop(Message& m) { protocol.plop(m);}
    private:
        T protocol;
};
class Plop{virtual void plop(Message&) = 0;} // Destruct or omitted for brevity
class upd:public Plop {/*Interface Plop*/void plop(Message&);};
class tcp:public Plop {/*Interface Plop*/void plop(Message&);};
class Service
{
    public:
        Service(Plop& p):protocol(p)  {};
        void doPlop(Message& m) { protocol.plop(m);}
    private:
        Plop& protocol;
};