0

我有以下结构:

template <typename T>
struct Odp
{
    T m_t;
};

我想对它进行专门化,这样我就可以添加一个运算符,这样该类型就可以很好地与 STL 集配合使用。(我不能Odp直接修改;它是遗留代码。)这是我看到的两种方法:

struct Ftw : public Odp<int>
{
    bool operator==(const Ftw& rhs)
    {
        return m_t == rhs.m_t;
    } 
};

struct FtwContain
{
    Odp<int> odp;
    bool operator==(const FtwContain& rhs)
    {
        return odp.m_t == rhs.odp.m_t;
    }
};

有什么理由更喜欢第二个而不是第一个?第一种方法似乎允许更简洁的代码:

Ftw ftw;
ftw.m_t = 2;

FtwContain ftwContain;
ftwContain.odp.m_t = 2;

(此外,我可能对“模板专业化”一词的含义感到困惑。)

4

6 回答 6

7

我认为不需要创建新类型 - 只需编写一个自由函数:

template <typename T>
bool operator==( const Odp<T> & a, const Odp <T> & b ) {
    return a.m_t == b.m_t;
}
于 2010-08-04T16:24:50.653 回答
1

您可能确实对术语感到困惑。(部分)模板专业化通常是指专用类型的模板化类/结构的特定实现。即,您可能有一个通用模板类Hash,它使用方法为类型提供哈希值getHash。然后这个方法有一个通用的实现,它不关心类型,也许还有一个字符串哈希值的特殊实现:

// default implementation 
template<typename T> class Hash { int getHash(T val) { return val; } }
// string implementation
template<> class Hash<std::string> { int getHash(std::string val) { return val[0] || val[1]; } }

但是,您在示例中所做的不是模板专业化,而是继承(在第一种方法中)并将Odp模板用作客户端。在这两种情况下,如果有人使用 Odp 模板Odp<int> odp,将使用原始实现,这可能不是您想要的。如果您要使用适当的模板专业化,Odp<int>请参考您的专业化代码。

于 2010-08-04T16:24:39.190 回答
0

正如尼尔所提到的,operator==很可能是一个免费的功能。

另一种选择:标准库允许使用自定义谓词对象。在这种情况下:

#include <set>

template <typename T>
struct Odp
{
    T m_t;
};

struct CompareOdp
{
    template <class T>
    bool operator() (const Odp<T>& a, const Odp<T>& b) const
    {
        return a.m_t < b.m_t;
    }
};

int main()
{
    std::set<Odp<int>, CompareOdp > my_set;
    Odp<int> value = {10};
    my_set.find(value);
}

(不确定,将整个谓词制作为模板是否是一个更好的主意。仅制作operator()一个模板似乎更容易使用,因为它给编译器留下了更多的东西来弄清楚。不确定它是否可以支持 -在某些情况下会起火。)


另请注意, std::set 使用谓词进行排序(默认情况下std::less<X>),而不是用于相等测试。

于 2010-08-04T17:33:54.740 回答
0

为什么不将 Odp 派生为 MyOdp,将您的(通用)代码放入其中,然后让 Ftw 从 Odp 派生(如在您的第一个示例中)或使用 typedef ?顺便说一句,不是专业化而是实例化。模板专业化是您(重新)为特定类型定义方法时。

于 2010-08-04T16:23:59.717 回答
0

我通常更喜欢组合而不是继承,但这实际上取决于设计。Ftw 是一种 Odp 还是 Ftw 包含 Odp。

我不会选择基于更清晰代码的方法(因为差别不大),我会根据概念上 Odp 和 Ftw 之间的关系来选择方法。

于 2010-08-04T16:24:01.980 回答
0

在您提到的情况下,我认为免费功能可能是重建问题最少的最干净的方法。把这个免费功能放在一个单独的 cpp 文件中,你应该很高兴。

可能的推导案例

  1. 如果您必须将对象传递给某个采用基类类型的函数,您会想要派生
  2. 派生类是否属于第一种类型。如果是这样,是的(例如,食肉动物是动物) 3. 如果您想在派生类中使用的基类中有受保护的方法。我不确定你提到的结构是完整的代码还是只是相关的部分。如果不是,那么这可能是您想要得出的原因之一。

包含的可能案例

  1. 您只想使用该类并且没有 is-a 关系。TBH,也可以模拟包含对象的 is-a,其中容器类型就像包含类型的代理(我认为这是一种设计模式,但不确定模式的名称)。
  2. 您只对使用一种或两种方法感兴趣,并且不用担心共享状态
  3. 该对象永远不会传递给需要基类的任何其他接口(总是可以传递包含的对象,但这看起来很脏。此外,折腾虚函数并且事情有所不同。对不起,我离题了)。
于 2010-08-04T16:34:22.573 回答