简体   繁体   English

避免模​​板特化中的构造函数重复

[英]avoid constructor duplication in template specialization

Assume I have a base class that stores a reference to some class Bar : 假设我有一个基类,它存储对某个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. 我想要做的是在此基础上添加一个继承级别,其中class Foo<T>将添加一些功能。

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. 然后,有一些Foo<T>实例需要提供不同版本的doSomething() ,因此使用模板特化。 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. 问题是,在Foo<>每个专用版本中,我必须重新实现构造函数并将Bar的引用传递给超类。 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. 本练习的目的是提供不同类型的doSomething() ,具有不同的签名。 So, without using C++11 (Because I'm stuck on GCC 4.6.3), is there a way to avoid this duplication of code? 所以,不使用C ++ 11(因为我坚​​持使用GCC 4.6.3),有没有办法避免这种重复的代码? Or, is there a better way of offering a different doSomething() ? 或者,是否有更好的方式提供不同的doSomething()

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. 我实际上认为SFINAE方法更好,但如果由于某种原因这对你不起作用,那么专门化类模板的各个成员函数可能对你有用。 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. 另一种选择是使用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: CRTP方法:

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

Instead of specialising Foo , you could supply every overload, and then enable the relevant overloads with SFINAE: 您可以提供每个重载,而不是专门化Foo ,然后使用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.) (由于您不能使用C ++ 11,请将std::enable_ifstd::is_same为boost版本或您自己的版本。)

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. 1)为您的输入定义一个抽象基类型,并在任何实现中使用doSomething。

bool doSomething(DoSomethingParamsBase* params);

or 要么

2) Use an enumerated MODE param with variadic params following 2)使用带有可变参数的枚举MODE参数

bool doSomething(MODE mode...);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM