使用特征来避免转发功能?

时间:2021-12-27 22:04:37

Let's say we have two classes, A and B. When using composition to model a "has-a" or "is-implemented-in-terms-of" relationship (e.g. B has-a A), one of the drawbacks vs. inheritance is that B does not the contain public functionality of A that it requires. In order to gain access to As public functions, it is necessary to provide forwarding functions (as opposed to inheritance, where B would inherit all of As public functions).

假设我们有两个类,A和B。当使用组合来建模“hasa”或“is-implemen -in-terms-of”关系(例如B hasa - A)的“has”或“is-implemen -in-terms-of”关系(例如B hasa)时,相对于继承的一个缺点是B没有A所需要的包含的公共功能。为了获得作为公共函数的访问权,需要提供转发函数(与继承相反,B将继承所有作为公共函数的函数)。

To give a more concrete example, let's say we have a Person which has-a ContactInfo:

举个更具体的例子,假设我们有一个人,他有联系信息:

using namespace std;

class ContactInfo
{
public:
   ContactInfo();
   void updateAddress(string address);
   void updatePhone(string phone);
   void updateEmail(string email);
private:
   string address;
   string phone;
   string email;
};

class Person
{
public:
   Person();
   // Forwarding functions:
   void updateAddress(string address){contactInfo.updateAddress(address)};
   void updatePhone(string phone){contactInfo.updatePhone(phone)};
   void updateEmail(string email){contactInfo.updateEmail(email)};
private:
   ContactInfo contactInfo;
};

Ignoring any deficiencies in the design (it is just a contrived example to demonstrate my question below), I have had to tediously replicate the exact function signatures from ContactInfo in Person. In a more complex example there could be many such functions, and many layers of composed classes, leading to much code duplication with all the usual problems of maintenance and being error-prone, etc.

忽略了设计中的任何不足(这只是一个经过设计的示例来演示我下面的问题),我不得不费力地亲自从ContactInfo复制确切的函数签名。在一个更复杂的示例中,可能有许多这样的函数,以及许多组成类的层,从而导致大量代码重复,导致所有常见的维护问题和容易出错等问题。

Nonetheless, this is the recommended practice for modelling "has-a" or "is-implemented-in-terms-of" according to sources such as Item 38 of Meyers' Effective C++, and Item 24 of Sutter's Exceptional C++ (link).

尽管如此,根据Meyers' s Effective C+的第38项和Sutter的the exception c++ (link)的第24项,这是为“hasa”或“is-implemen -implemen -of”建模的推荐实践。

Whilst researching this, I came across this Wikipedia article which discusses the same topic. At the bottom of the article, it suggests the following:

在研究这个问题时,我偶然发现了*上的一篇文章,它讨论的是同一个话题。在文章的最后,它建议如下:

One drawback to using composition in place of inheritance is that all of the methods being provided by the composed classes must be implemented in the derived class, even if they are only forwarding methods. [...] This drawback can be avoided by using traits.

使用组合替代继承的一个缺点是,组合类提供的所有方法都必须在派生类中实现,即使它们只是转发方法。[…[英语背诵文选利用特征可以避免这种缺点。

I am fairly new to the concept of traits and with everything I have read, I am finding it hard to relate to the above statement. My question therefore is: How would one go about using traits to avoid forwarding functions with composition? An answer based on my example (Person and ContactInfo) would be ideal.

我对特质的概念相当陌生,而且我读到的所有东西都很难和上面的说法联系起来。因此,我的问题是:如何使用特征来避免使用组合来转发函数?基于我的示例(Person和ContactInfo)的答案将是理想的。

EDIT: Just to clarify, in response to some of the answers, I am aware of private inheritance as an alternative to composition for modelling "is-implemented-in-terms-of". My question is not about that, it is specifically about the meaning of Wikipedia's statement relating to traits. I am not asking for alternatives to composition. I've bolded my question to make it clearer that this is what I'm asking.

编辑:为了澄清一些问题,我意识到私有继承是建模“is- implementation -in- of”的替代方法。我的问题不是这个,而是*关于特质的声明的意义。我并不是在寻找写作的替代品。我已经把我的问题说得更清楚了,这就是我要问的问题。

4 个解决方案

#1


1  

First of all I should mention that traits are different things in C++/STL and languages like PHP, Lasso etc. It looks like the article from wikipedia refers to PHP-like traits because C++/STL traits are not designed for polymorphic reuse (we are speaking about code reuse with polymorphic behavior, right?). They designed to simplify declaration of template classes.

首先我要指出的是,在c++ /STL和PHP、Lasso等语言中,特征是不同的。看起来*上的文章提到了PHP类特征,因为c++ /STL特征不是为多态重用而设计的(我们说的是代码重用和多态行为,对吧?)它们旨在简化模板类的声明。

Traits are used in some languages that don't support multiple inheritance (PHP, Lasso etc). Traits allow to "emulate" multiple inheritance (but multiple inheritance and traits are not exactly the same).

在某些不支持多重继承(PHP、Lasso等)的语言中,可以使用特征。特征允许“模拟”多重继承(但是多重继承和特征并不完全相同)。

In contrast C++ doesn't support PHP-traits but supports multiple inheritance. So if speaking about C++ then trait-like solution will be something like this:

相反,c++不支持php特性,而是支持多重继承。如果说到c++,那么类图解应该是这样的

class VisibleTrait
{
    public:
        virtual void draw();
};

class SolidTrait
{
    public:
        virtual void collide(Object objects[]);
};

class MovableTrait
{
    public:
        virtual void update();
};


// A player is visible, movable, and solid
class Player : public VisibleTrait, MovableTrait, SolidTrait
{
};

// Smoke is visible and movable but not solid 
class Smoke : public VisibleTrait, MovableTrait
{
};

// A hause is visible and solid but not movable
class House : public VisibleTrait, SolidTrait
{
};

So to answer your question

回答你的问题

How would one go about using traits to avoid forwarding functions with composition?

如何使用特征来避免使用组合来转发函数?

1) Traits don't avoid forwarding functions with composition because traits work independently from composition. (The article from Wikipadia is misleading a little regarding the relationship between traits and composition)
2) PHP/Lasso-like traits can be partially emulated in C++ with multiple inheritance.

1)特质并不会避免转发的功能,因为特质是独立于构图的。(这篇来自Wikipadia的文章对性状和组合之间的关系有一点误导)2)PHP/ lasso类性状可以在c++中通过多重继承部分模拟。

#2


2  

Maybe you can try private inheritance:

也许你可以尝试私有继承:

class Person : private ContactInfo
{
public:
   Person() { }
   using ContactInfo::updateAddress;
   using ContactInfo::updatePhone;
   using ContactInfo::updateEmail;
};

int main()
{
    Person person;
    person.updateAddress("hi");
    return 0;
}

Although you may want to watch out for the caveats listed in this FAQ:

尽管您可能想要注意本FAQ中列出的注意事项:

There are also several distinctions:

还有一些区别:

  • The simple-composition variant is needed if you want to contain several Engines per Car
  • 如果您想要每辆车包含几个引擎,则需要简单的组合变体。
  • The private-inheritance variant can introduce unnecessary multiple inheritance
  • 私有继承变体可以引入不必要的多重继承
  • The private-inheritance variant allows members of Car to convert a Car* to an Engine*
  • 私有继承的变体允许汽车的成员将汽车转换为引擎*。
  • The private-inheritance variant allows access to the protected members of the base class
  • 私有继承变体允许访问基类的受保护成员
  • The private-inheritance variant allows Car to override Engine's virtual functions
  • 私有继承变量允许Car重写引擎的虚函数。
  • The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine's start() method
  • 私有继承变体使为Car提供一个只调用引擎的start()方法变得稍微简单一些(20个字符与28个字符相比)

Otherwise the example of composition provided seems identical to yours. Clicking on the traits link in the wikipedia article didn't provide any C++ articles, and the link in the references seems to be about type traits. I couldn't find how type traits has anything to do with your scenario.

否则,所提供的作文范例似乎与你的一模一样。单击*文章中的traits链接没有提供任何c++文章,引用中的链接似乎是关于类型特征的。我找不出类型特征和你的场景有什么关系。

#3


1  

The article talk about inheritance with Interface,

本文讨论了接口的继承,

so in fact it tells that the object have to respect some signatures.

事实上,它告诉我们对象必须尊重一些签名。

type traits can be used to check if signature is correct and dispatch to appropriate function

类型特征可用于检查签名是否正确,并分派到适当的函数

For example some STL algorithms wait for type Iterator, but those iterators don't inherit from a class Iterator but must provide some contract (operator ++(), operator !=(rhs), operator*()).

例如,一些STL算法等待类型迭代器,但是这些迭代器不从类迭代器继承,而是必须提供一些契约(操作符+()、操作符!=(rhs)、操作符*())。

with the article example:

本文的例子:

  • class Player - which can Move
  • 职业球员-可以移动
  • class Building - which can not Move
  • 类楼-不能移动。

And code:

和代码:

#if 1
// simple type traits which tells if class has method update_position
template <typename T> struct can_move;

// Hardcode the values
template <> struct can_move<Player> { static const bool value = true; };
template <> struct can_move<Building> { static const bool value = false; };

#else
// or even better, but need a has_update_position. (see how to check if member exist in a class)
template <typename T> struct can_move{ static const bool value = has_update_position<T>::value };
#endif

template <typename T, bool> struct position_updater;

// specialization for object which can move
template <typename T> struct position_updater<T, true>
{
    static void update(T& object) { object.update_position(); }
};

// specialization for object which can NOT move
template <typename T> struct position_updater<T, false>
{
    static void update(T& object) { /* Do nothing, it can not move */ }
};


template <typename T>
void update_position(T& object)
{
    // statically dispatch to the correct method
    // No need of interface or inheritance
    position_updater<T, can_move<T>::value>::update(object);
}

#4


0  

AFAIK a trait class is something like the following:

特征类是这样的:

#include <iostream>
using namespace std;

class ContactInfo
{
public:
   void updateAddress() { cout << "update address"; };
   void updatePhone() {};
   void updateEmail() {};
};

template<class T> class TraitClass
{
  public:

  private:
    T obj;
};

template<> class TraitClass<ContactInfo>
{
  public:
        void updateAddress() {obj.updateAddress();};
        void updatePhone() {obj.updatePhone();};
        void updateEmail() {obj.updateEmail();};
  private:
    ContactInfo obj;
};

class Person
{
public:
        void updateAddress() {obj.updateAddress();};
        void updatePhone() {obj.updatePhone();};
        void updateEmail() {obj.updateEmail();};
private:
    TraitClass<ContactInfo> obj;
};

int main() {

    Person myPerson;

    myPerson.updateAddress();

    return 0;
}

That is: a compile-time templated class where you can implement (and/or specialize it) your delegate methods in order to forward whatever you need without (for whatever reason you have) recurring to inheritance.

也就是说:一个编译时模板类,在这个类中,您可以实现(和/或专门化)您的委托方法,以便将您需要的任何东西(无论出于什么原因)转发给继承。

http://en.wikipedia.org/wiki/Trait_(computer_programming)

http://en.wikipedia.org/wiki/Trait_(computer_programming)

#1


1  

First of all I should mention that traits are different things in C++/STL and languages like PHP, Lasso etc. It looks like the article from wikipedia refers to PHP-like traits because C++/STL traits are not designed for polymorphic reuse (we are speaking about code reuse with polymorphic behavior, right?). They designed to simplify declaration of template classes.

首先我要指出的是,在c++ /STL和PHP、Lasso等语言中,特征是不同的。看起来*上的文章提到了PHP类特征,因为c++ /STL特征不是为多态重用而设计的(我们说的是代码重用和多态行为,对吧?)它们旨在简化模板类的声明。

Traits are used in some languages that don't support multiple inheritance (PHP, Lasso etc). Traits allow to "emulate" multiple inheritance (but multiple inheritance and traits are not exactly the same).

在某些不支持多重继承(PHP、Lasso等)的语言中,可以使用特征。特征允许“模拟”多重继承(但是多重继承和特征并不完全相同)。

In contrast C++ doesn't support PHP-traits but supports multiple inheritance. So if speaking about C++ then trait-like solution will be something like this:

相反,c++不支持php特性,而是支持多重继承。如果说到c++,那么类图解应该是这样的

class VisibleTrait
{
    public:
        virtual void draw();
};

class SolidTrait
{
    public:
        virtual void collide(Object objects[]);
};

class MovableTrait
{
    public:
        virtual void update();
};


// A player is visible, movable, and solid
class Player : public VisibleTrait, MovableTrait, SolidTrait
{
};

// Smoke is visible and movable but not solid 
class Smoke : public VisibleTrait, MovableTrait
{
};

// A hause is visible and solid but not movable
class House : public VisibleTrait, SolidTrait
{
};

So to answer your question

回答你的问题

How would one go about using traits to avoid forwarding functions with composition?

如何使用特征来避免使用组合来转发函数?

1) Traits don't avoid forwarding functions with composition because traits work independently from composition. (The article from Wikipadia is misleading a little regarding the relationship between traits and composition)
2) PHP/Lasso-like traits can be partially emulated in C++ with multiple inheritance.

1)特质并不会避免转发的功能,因为特质是独立于构图的。(这篇来自Wikipadia的文章对性状和组合之间的关系有一点误导)2)PHP/ lasso类性状可以在c++中通过多重继承部分模拟。

#2


2  

Maybe you can try private inheritance:

也许你可以尝试私有继承:

class Person : private ContactInfo
{
public:
   Person() { }
   using ContactInfo::updateAddress;
   using ContactInfo::updatePhone;
   using ContactInfo::updateEmail;
};

int main()
{
    Person person;
    person.updateAddress("hi");
    return 0;
}

Although you may want to watch out for the caveats listed in this FAQ:

尽管您可能想要注意本FAQ中列出的注意事项:

There are also several distinctions:

还有一些区别:

  • The simple-composition variant is needed if you want to contain several Engines per Car
  • 如果您想要每辆车包含几个引擎,则需要简单的组合变体。
  • The private-inheritance variant can introduce unnecessary multiple inheritance
  • 私有继承变体可以引入不必要的多重继承
  • The private-inheritance variant allows members of Car to convert a Car* to an Engine*
  • 私有继承的变体允许汽车的成员将汽车转换为引擎*。
  • The private-inheritance variant allows access to the protected members of the base class
  • 私有继承变体允许访问基类的受保护成员
  • The private-inheritance variant allows Car to override Engine's virtual functions
  • 私有继承变量允许Car重写引擎的虚函数。
  • The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine's start() method
  • 私有继承变体使为Car提供一个只调用引擎的start()方法变得稍微简单一些(20个字符与28个字符相比)

Otherwise the example of composition provided seems identical to yours. Clicking on the traits link in the wikipedia article didn't provide any C++ articles, and the link in the references seems to be about type traits. I couldn't find how type traits has anything to do with your scenario.

否则,所提供的作文范例似乎与你的一模一样。单击*文章中的traits链接没有提供任何c++文章,引用中的链接似乎是关于类型特征的。我找不出类型特征和你的场景有什么关系。

#3


1  

The article talk about inheritance with Interface,

本文讨论了接口的继承,

so in fact it tells that the object have to respect some signatures.

事实上,它告诉我们对象必须尊重一些签名。

type traits can be used to check if signature is correct and dispatch to appropriate function

类型特征可用于检查签名是否正确,并分派到适当的函数

For example some STL algorithms wait for type Iterator, but those iterators don't inherit from a class Iterator but must provide some contract (operator ++(), operator !=(rhs), operator*()).

例如,一些STL算法等待类型迭代器,但是这些迭代器不从类迭代器继承,而是必须提供一些契约(操作符+()、操作符!=(rhs)、操作符*())。

with the article example:

本文的例子:

  • class Player - which can Move
  • 职业球员-可以移动
  • class Building - which can not Move
  • 类楼-不能移动。

And code:

和代码:

#if 1
// simple type traits which tells if class has method update_position
template <typename T> struct can_move;

// Hardcode the values
template <> struct can_move<Player> { static const bool value = true; };
template <> struct can_move<Building> { static const bool value = false; };

#else
// or even better, but need a has_update_position. (see how to check if member exist in a class)
template <typename T> struct can_move{ static const bool value = has_update_position<T>::value };
#endif

template <typename T, bool> struct position_updater;

// specialization for object which can move
template <typename T> struct position_updater<T, true>
{
    static void update(T& object) { object.update_position(); }
};

// specialization for object which can NOT move
template <typename T> struct position_updater<T, false>
{
    static void update(T& object) { /* Do nothing, it can not move */ }
};


template <typename T>
void update_position(T& object)
{
    // statically dispatch to the correct method
    // No need of interface or inheritance
    position_updater<T, can_move<T>::value>::update(object);
}

#4


0  

AFAIK a trait class is something like the following:

特征类是这样的:

#include <iostream>
using namespace std;

class ContactInfo
{
public:
   void updateAddress() { cout << "update address"; };
   void updatePhone() {};
   void updateEmail() {};
};

template<class T> class TraitClass
{
  public:

  private:
    T obj;
};

template<> class TraitClass<ContactInfo>
{
  public:
        void updateAddress() {obj.updateAddress();};
        void updatePhone() {obj.updatePhone();};
        void updateEmail() {obj.updateEmail();};
  private:
    ContactInfo obj;
};

class Person
{
public:
        void updateAddress() {obj.updateAddress();};
        void updatePhone() {obj.updatePhone();};
        void updateEmail() {obj.updateEmail();};
private:
    TraitClass<ContactInfo> obj;
};

int main() {

    Person myPerson;

    myPerson.updateAddress();

    return 0;
}

That is: a compile-time templated class where you can implement (and/or specialize it) your delegate methods in order to forward whatever you need without (for whatever reason you have) recurring to inheritance.

也就是说:一个编译时模板类,在这个类中,您可以实现(和/或专门化)您的委托方法,以便将您需要的任何东西(无论出于什么原因)转发给继承。

http://en.wikipedia.org/wiki/Trait_(computer_programming)

http://en.wikipedia.org/wiki/Trait_(computer_programming)