提高可读性和可维护性:省略 < > 是否可以用于许多变量声明?

Increase readability & maintainability : omit < > for many variable declaration possible?

这个问题看起来很抽象。我会通过一个例子来问。

简介

假设我有很多类型的游戏 objects.

它们是 Bullet、Rocket、Enemy、Zone、...。

它们都是由池很好地创建、删除和管理的,例如

Pool<Bullet> poolBullet ; 
Pool<Rocket> poolRocket ;  

游戏逻辑将以 Pool_Handle 的形式管理 object,例如

Pool_Handle< Bullet >  bullet = poolBullet.create() ;
Pool_Handle< Rocket>  rocket = poolRocket .create() ;

问题

现在,我看看重构。

例如,如果我的旧代码是...

Bullet* functionA(Rocket* rocket){
    for(int n=0;n<rocket->zones.size();n++){
        Zone* zone = rocket->zones.get(n);
        Bullet* return_value =zone.functionB();
    }
}

...它会变成...

Pool_Handle<Bullet> functionA(Pool_Handle<Rocket> rocket){
    for(int n=0;n<rocket->zones.size();n++){
        Pool_Handle<Zone> zone = rocket->zones.get(n);
        Pool_Handle<Bullet> return_value =zone.functionB();
    }
}

请注意,它们现在 Pool_Handle 无处不在。

看了几天编辑了这几千行代码,我对Pool_Handle的习惯甚至超过了任何游戏object。可能是我现在打字最快的词了

问题

如何保持旧代码的可读性和可维护性,如果可能,如何减少变量模板的输入时间?

我不期待一个完美的答案,因为我找到的每个解决方案都有一些 trade-off。

我糟糕的解决方案

/**  remedy 1 **/
template <class TT> using H = Pool_Handle<TT>; //remedy line
H<Bullet> bullet ; //alleviate symptom, better but not perfect

/**  remedy 2 **/
using H_Bullet  = Pool_Handle<H_Bullet>; //remedy line
H_Bullet bullet ; //looks good, but have limitations

/** remedy 3 **/
auto bullet;  //concise, but can't be used everywhere, sometimes also reduce readability

第二个解决方案似乎不错,但引入了一些限制。

有没有更好的方法?

(编辑)您可以假定 c++11、c++14 等等。
(编辑)独立于平台的解决方案是有利的。

编辑 1(进一步阐明为什么第二种解决方案不完美)

在第二个解决方案中,我必须在声明新 class 后添加“另一行”。

该行是...

using H_Bullet  = Pool_Handle<H_Bullet>; 

我应该把它放在哪里?

  1. 里面 Bullet.h ;

    它会造成不良耦合,因为 Bullet 根本不应该知道 Handle。

  2. 在每个游戏逻辑文件中包含的一些 high-level header ;

    结果是有2个不同的地方对Bullet有一些定义。
    更多的地方 -> 更少的可维护性。

这两个地方都会产生一定的小缺点: 当我调用一些自动重构时,我必须在 BulletH_Bullet.

上调用两次

选项 1

使用 auto 说明符:

auto object = pool.create();

完整示例:

template <typename T>
class Pool {
public:
  class Handle {};

  Handle create () const {
    return Handle();
  }
};

class Object {};

int main () {
  Pool<Object> pool;
  auto object = pool.create();
}

View Successful Compilation Result

选项 2

使用 typedef(对于无法使用 功能的用户):

Object::Handle object = pool.create();

完整示例:

template <typename T>
class Pool {
public:
  class Handle {};

  Handle create () const {
    return Handle();
  }
};

class Object {
public:
  typedef Pool<Object>::Handle Handle;
};

int main () {
  Pool<Object> pool;
  Object::Handle object = pool.create();
}

View Successful Compilation Result

除了使用 auto,您还可以受益于使用 STL 算法而不是手写循环。在您的示例中,而不是

Pool_Handle<Bullet> functionA(Pool_Handle<Rocket> rocket){
    for(int n=0;n<rocket->zones.size();n++){
        Pool_Handle<Zone> zone = rocket->zones.get(n);
        Pool_Handle<Bullet> return_value =zone.functionB();
    }
}

你可以直接使用std::for_each

std::for_each( rocket->zones.begin(), rocket->zones.end(), 
               [](auto const& z) {z.functionB();} );

对于这个简单的例子,C++11 for-each 循环可能更好:

for(auto const& z: rocket->zones) {
    z.functionB();
}

总的来说,我会强烈建议查看范围库,例如Boost.Range or Eric Niebler's range-v3。这使您能够编写非常简洁但描述性强的代码,例如

// call functionB on all zones and sum the results
auto sumOfResult = accumulate( rocket->zones | transformed( [](auto const& z) { return functionB(z);}) );

刚刚向前声明您感兴趣的类型的 header 有什么问题?它只会在您向系统添加需要句柄的新类型时发生变化,并且如果您重命名/添加某些内容,它将正常中断。

types_fwd.hpp

template <typename> Pool;
template <typename> Pool_Handle;

class Bullet;
class Rocket;

using H_Bullet = Pool_Handle<Bullet>;
using H_Rocket = Pool_Handle<Rocket>;

唯一的其他选择是反转事物并向前声明池和句柄,并将其包含/添加到每个 header 中,引入需要句柄的新类型。