Question

Assume I have a base class that stores a reference to some class Bar:

class FooBase
{
public:
  FooBase( Bar &ctx ) : _barCtx( ctx ) {};
  virtual ~FooBase() {};

  // Some other functions

protected:
  Bar &_barCtx;
};

What I'd like to do is add a level of inheritance on top of this, where class Foo<T> will add some functionality.

template< typename T >
class Foo : public FooBase
{
public:
  Foo( Bar &ctx ) : FooBase( ctx ) {};
  bool doSomething( int a );
};

Then, there are some instances of Foo<T> that need to offer a different version of doSomething(), so template specialization is used. The problem is, in each specialized version of Foo<>, I have to re implement the constructor and pass the reference of Bar to the super class. This is basically copy and paste code, which I'd like to avoid.

class Baz;

template<>
class Foo<Baz> : public FooBase
{
public:
  Foob( Bar &ctx ) : FooBase( ctx ) {};
  bool doSomething( std::string &str, int x, float g );
};

The point of this exercise is to offer a different type of doSomething(), with a different signature. So, without using C++11 (Because I'm stuck on GCC 4.6.3), is there a way to avoid this duplication of code? Or, is there a better way of offering a different doSomething()?

Was it helpful?

Solution

I actually think a SFINAE approach is better, but if that doesn't work for you for some reason, then specializing individual member functions of a class template might work for you. However, you'll have to declare all overloads in the generic template, then provide the definitions as appropriate. This will make sure that you will get link errors if you call the wrong overloads.

The other option is to use CRTP. That approach is shown further below.

Member specialization approach:

#include <string>

class Bar {};

class FooBase
{
public:
  FooBase( Bar &ctx ) : _barCtx( ctx ) {};
  virtual ~FooBase() {};
protected:
  Bar &_barCtx;
};

template< typename T >
class Foo : public FooBase
{
public:
  Foo( Bar &ctx ) : FooBase( ctx ) {};
  bool doSomething( int a ) { return true; }
  // Declared, but not defined.
  bool doSomething( std::string &str, int x, float g );
};

class Baz {};

// Declared, but not defined.
template <>
bool
Foo<Baz>::doSomething(int i);

template <>
bool
Foo<Baz>::doSomething(std::string &str, int x, float g) {
    return true;
}

int main() {
    Bar b;
    Foo<int> f1(b);
    std::string s;

    f1.doSomething(1); // Compiles.
    // f1.doSomething(s, 1, 3.14f); // Link error.

    Foo<Baz> f2(b);
    // f2.doSomething(1); // Link error.
    f2.doSomething(s, 1, 3.14f); // Compiles.
}

CRTP approach:

#include <string>

class Bar {};
class Baz {};

template <typename T>
class Spec {
    public:
        bool doSomething( int a );
};

template <>
class Spec<Baz> {
    public:
        bool doSomething( std::string &str, int x, float g );
};

class FooBase {
    public:
        FooBase( Bar &ctx ) : _barCtx( ctx ) {};
        virtual ~FooBase() {};
    protected:
        Bar &_barCtx;
};

template< typename T >
class Foo : public FooBase, public Spec<T> {
    public:
        Foo( Bar &ctx ) : FooBase( ctx ) {};
};

template <typename T>
bool Spec<T>::doSomething( int a ) {
    Foo<T> *fp = static_cast<Foo<T> *>(this);
    return true;
}

bool Spec<Baz>::doSomething( std::string &str, int x, float g ) {
    Foo<Baz> *fp = static_cast<Foo<Baz> *>(this);
    return true;
}

int main() {

    Bar b;
    std::string s;

    Foo<int> f1(b);
    f1.doSomething(1);

    Foo<Baz> f2(b);
    f2.doSomething(s, 1, 3.14f);
}

OTHER TIPS

Instead of specialising Foo, you could supply every overload, and then enable the relevant overloads with SFINAE:

template< typename T >
class Foo : public FooBase
{
public:
  Foo( Bar &ctx ) : FooBase( ctx ) {};

  template<
    typename U = T,
    typename = typename std::enable_if<!std::is_same<U, Baz>::value>::type>
  bool doSomething( int a )
  {
      std::cout << "doSomething( int a )\n";
  }

  template<
      typename U = T,
      typename = typename std::enable_if<std::is_same<U, Baz>::value>::type>
  bool doSomething( std::string &str, int x, float g )
  {
      std::cout << "doSomething( std::string &str, int x, float g )\n";
  }
};

(Since you can't use C++11, replace std::enable_if and std::is_same with boost versions or your own versions.)

This really seems like the wrong place for using template specialization. The templated type is not being used anywhere in the declaration, so it comes off as completely arbitrary.

I would suggest using other techniques

1) Define an abstract base type for your inputs, and have doSomething take in any implementation of that.

bool doSomething(DoSomethingParamsBase* params);

or

2) Use an enumerated MODE param with variadic params following

bool doSomething(MODE mode...);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top