10

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).

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.

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).

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.

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.

JBentley
  • 6,099
  • 5
  • 37
  • 72
  • I think wikipedia is misleading you on this. Traits are a tool of generic programming, say you wanted to contain `contactInfo` of some template type parameter type. – user1095108 Jan 04 '14 at 11:39
  • 1
    "Has a" relationship is a "has a" relationship. There are no "contrived examples" because in case of any sitaution it's possible to solve the problem of deciding if somethin IS something or it HAS something. If you are unable to solve this then you are making mistake elsewhere. A tricky example would be like this: A car is a vehicle, an aircraft is a vehicle so is trains a vehicle? The answer is: no. Trains is a set of vehicles (the front engine, the wagons, which also can move and optionally the back vehicle). You always have to make clear distinctions otherwise you will get into trouble. – luke1985 Jan 04 '14 at 12:19
  • I don't find this Wikipedia article good :-/ – Jarod42 Jan 04 '14 at 18:57
  • @lukasz1985 Thanks, but my question wasn't about how to differentiate between "has a" and "is a", it was about traits. – JBentley Jan 04 '14 at 22:05
  • I dont think you should use traits to get your job done easier. You are probably messing up the composition because in your example you should be able to get the underlying object by using the containing class (like Person->getContactInfo() ). The good thing is not to use traits and multiple inheritance first. And if going this way the only thats left is the matter of decding between inheritance and composition (IS A or HAS A). You can look at my answer here: http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance/20921914#20921914 – luke1985 Jan 04 '14 at 22:50
  • You cannot decide about this things arbitrary, "prefer one thing over the other" , the quote: >>>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.<<< Is ridiculous because composition(HAS A) and inheritance(IS A) cannot be compared to each other. Traits can only put a lot of bury on your code making it very random and chaotic (trait here, trait there, trait everywhere). – luke1985 Jan 04 '14 at 23:01
  • @lukasz1985 As I said, my question is not about how to decide or prefer one method over another. I specifically said my example is contrived so that people wouldn't nitpick it (i.e. it doesn't matter whether or not my example is a good candidate for composition - it is just a set up to lead into my question). But I do appreciate your comments regarding traits. – JBentley Jan 05 '14 at 10:31
  • I'll tell you one last thing: OOP is a programming paradigm in which traits, mutliple inheritance and interfaces(interfaces - with some exceptions) have no right to exist. If you use them it will pay on you later very painfully. – luke1985 Jan 05 '14 at 10:55

4 Answers4

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:

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
  • 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

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.

2

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*()).

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);
}
pr0d1g1
  • 70
  • 5
Jarod42
  • 203,559
  • 14
  • 181
  • 302
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.

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).

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:

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.

Alex Antonov
  • 942
  • 5
  • 9
  • Thank you for providing an answer which addresses my question directly. – JBentley Jan 04 '14 at 22:10
  • @JBentley, traits in c++ can be used in order to provide better method forwarding with composition. See my related SO question here : http://stackoverflow.com/questions/28393195/method-forwarding-with-composition-instead-of-inheritance-using-c-traits – Julien__ Feb 08 '15 at 13:32
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)

Marco A.
  • 43,032
  • 26
  • 132
  • 246