C++:模板化类的继承与包含

发布于 2024-09-13 08:46:08 字数 674 浏览 10 评论 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;

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

I have the following struct:

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

I want to specialize it so I can add an operator so the type plays nicely with STL sets. (I can't modify Odp directly; it's legacy code.) Here are two methods I see of doing it:

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;
    }
};

Is there any reason to prefer the second over the first? The first method appears to allow cleaner code:

Ftw ftw;
ftw.m_t = 2;

FtwContain ftwContain;
ftwContain.odp.m_t = 2;

(Also, there's a chance that I'm confused about what the term "template specialization" means.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

您的好友蓝忘机已上羡 2024-09-20 08:46:08

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

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

I don't believe there is any need to create a new type - simply write a free function:

template <typename T>
bool operator==( const Odp<T> & a, const Odp <T> & b ) {
    return a.m_t == b.m_t;
}
遗心遗梦遗幸福 2024-09-20 08:46:08

您可能确实对这些术语感到困惑。 (部分)模板专门化通常指的是模板化类/结构的特定实现专用类型。即,您可能有一个通用模板类 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中所示, odp,将使用原始实现,这可能不是您想要的。如果您使用正确的模板专业化,Odp 将引用您的专业代码。

You may indeed be confused about the terminilogy. (Partial) template specialization normally referes to a specific implementation of a templated class /struct for a dedicated type. I.e. you may have a generic template class Hash that provides hash values for types using a method getHash. This method then has a generic implementation, that doesn't care about the type, and maybe a special implementation for hash values on strings:

// 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]; } }

What you are doing in ur examples however is not template specialization but inheritance (in the first approach) and using the Odp template as a client. In both cases, if anyone uses the Odp template as in Odp<int> odp, the original implementation will be used, which may not be what you want. If you would use proper template specialization, Odp<int> would refer to your specialized code.

兔小萌 2024-09-20 08:46:08

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

Why not deriving Odp to MyOdp, put your (generic) code in it and just make Ftw derive from Odp (as in your first example) or using a typedef ?
By the way that not specialization but instanciation. Template specialization is when you (re)define a method for a specific type.

飘逸的'云 2024-09-20 08:46:08

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

我不会选择基于更清晰的代码的方法(因为它没有太大区别),我会选择基于概念上 Odp 和 Ftw 之间的关系的方法。

I usually prefer composition over inheritance, but it really depends on the design. Is Ftw a type of Odp or does Ftw contain an Odp.

I wouldn't choose the method based on cleaner code (since it's not that much of a difference), I would choose the method based on conceptually what is the relationship between Odp and Ftw.

如果没有 2024-09-20 08:46:08

在您提到的情况下,我认为免费功能可能是最干净的方式,并且重建问题最少。将此免费函数放入单独的 cpp 文件中,您应该可以开始使用了。

派生的可能情况

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

包含的可能情况

  1. 您只想​​使用该类,并且不存在 is-a 关系。 TBH,我们也可以模拟包含对象的 is-a,其中容器类型充当包含类型的代理(我认为这是一种设计模式,但不确定该模式的名称)。
  2. 您只对使用一两个方法感兴趣,并且不用担心共享状态。
  3. 该对象永远不会传递给任何其他需要基类的接口(人们总是可以传递包含的对象,但这看起来很脏。此外,扔进虚拟函数,事情就不同了。抱歉,我离题了)。

In the case you mentioned, I think a free function is possibly the cleanest way with the least amount of rebuild issues. Put this free function in a separate cpp file and you should be good to go.

Possible cases for derivation

  1. You would want to derive if you have to pass your object to some function which takes a base-class type
  2. Is the derived class a type of the first type. If so, yes (eg., a carnivore is an animal)
    3.If there are protected methods in the base class that you want to use in your derived class. I am not sure if the structure you mentioned is the complete code or only the relevant section. If it is not, then this might be one reason you want to derive.

Possible cases for containing

  1. You merely want to use the class and there is no is-a relationship. TBH, one can simulate an is-a with containing objects too, where in the container type acts like a proxy for the contained-type (I think this is a design pattern, but am not sure of the name of the pattern).
  2. You are interested in using only one or two methods, and there is no worry of a shared state
  3. This object is never passed to any other interface which requires a base class (one can always pass the contained object, but that looks dirty. Also, toss in virtual functions and things are different. Sorry, I digress).
狼性发作 2024-09-20 08:46:08

正如 Neil 提到的,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) ,不适用于平等测试。

As Neil mentions, operator== can well be a free function.

Another option: standard library allows the use of custom predicate objects. In this case:

#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);
}

(Not sure, whether it might be a better idea to make the whole predicate a template. Making just operator() a template seems to make it easier to use, as it leaves more things to the compiler to figure out. Not sure if it could back-fire in some scenarios.)


Also note that std::set uses a predicate for ordering (by default std::less<X>), not for equality tests.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文