使相同的C ++类型别名不兼容

时间:2016-12-23 11:13:18

标签: c++ c++11

我将std::vector<int>用于两种不同的信息。我想确保我不会意外混合两种用途。

简而言之,我想要像这段代码一样失败:

#include <vector>

using A = std::vector<int>;
using B = std::vector<int>;

void fa(const A&);
void fb(const B&);

void fun()
{
    A ax;
    B bx;

    fa(bx);
    fb(ax);
}

即使fa期望参数类型为A,此代码也会编译。 显然,AB是完全相同的。

使这段代码正确编译的最简单方法是什么:

fa(ax);
fb(bx);

并使此代码失败:

fa(bx);
fb(ax);

当然,我可以将std::vector<int>包装在另一个类中,但是我需要重写它的接口。或者,我可以继承std::vector<int>,但经常不鼓励这样做。

简而言之,我需要两个不兼容的std::vector<int>版本。

修改

有人建议Strong typedefs可以解决这个问题。这只是部分正确。如果我使用BOOST_STRONG_TYPEDEF(std::vector<int>, A),我需要添加一些恼人的演员表。例如,而不是

A ax{1,3,5};

我需要使用

A ax{std::vector<int>{1,3,5}};

而不是

for (auto x : ax) ...

我需要使用

for (auto x : (std::vector<int>)ax) ...

4 个答案:

答案 0 :(得分:58)

我认为你想要的仍然是最好的:

struct A : public std::vector<int>{
  using vector::vector;
};
struct B : public std::vector<int>{
  using vector::vector;
};

它完全符合您的要求。为避免干净的陈述,没有理由想出一些丑陋的hackery。我认为这种子类型不受欢迎的主要原因是相同的事物应该表现得像它们是相同的并且可以互换使用。但这正是你想要压制的东西,因此对它进行分类就可以完全符合你想要的陈述:它们具有相同的界面,但它们不应该被使用,因为它们不是一样的。

答案 1 :(得分:14)

这种或那种方式,这是primitive obsession的情况。要么int s代表某种东西而vector s是这个东西的集合,要么vector<int>代表某种东西。

在这两种情况下,都应该通过将基元包装成更有意义的东西来解决。例如:

class column
{
  int id;
  /*...*/
}; 
class row
{
  int id;
  /*...*/
};

std::vector<row>std::vector<column>不可互换。

当然,如果vector<int>是真正意味着其他内容的原语,那么相同的想法可以应用于int而不是vector<int>

答案 2 :(得分:4)

  

或者,我可以从std :: vector继承,但经常不鼓励这样做。

IMO,这取决于具体情况。一般来说可能是一个很好的解决方案

#include <vector>

class VectorA :
    public std::vector<int> {
 public:
  VectorA() = default;
  ~VectorA() = default;
  VectorA(const VectorA&) = default;
  VectorA(VectorA&&) = default;
  VectorA& operator=(const VectorA&) = default;
  VectorA& operator=(VectorA&&) = default;
};

class VectorB :
    public std::vector<int> {
 public:
  VectorB() = default;
  ~VectorB() = default;
  VectorB(const VectorB&) = default;
  VectorB(VectorB&&) = default;
  VectorB& operator=(const VectorB&) = default;
  VectorB& operator=(VectorB&&) = default;
};

您仍然可以使用VectorAVectorB作为普通向量,但不能在它们之间切换。

void acceptA(const VectorA& v) {
  // do something
}

void acceptB(const VectorB& v) {
  // do something
}

template<typename T>
void acceptVector(const std::vector<T>& v) {
  // do something
}

int main(int argc, char *argv[]) {
  VectorA va;
  VectorB vb;

  acceptA(va);  // you can only pass VectorA
  acceptB(vb);  // same here for VectorB

  acceptVector(va);  // any vector
  acceptVector(vb);

  return 0;
}

答案 3 :(得分:4)

这就是为什么你可以在C ++中进行面向对象编程以及重用库类型的基于对象的编程的部分原因。

制作A和B类,为您的域中的行为建模。如果两个行为都是使用作为int的向量的字段实现的,那么这并不重要;只要你不打破封装,对不同向量的所有操作都将在他们的类的范围内,并且不会发生混淆。

#include <vector>

class A {
    std::vector<int> cake_orders_;

public:
    void f() ; // can only do something to do with cake
};

class B {
    std::vector<int> meal_worm_lengths_;

public:
    void f() ; // can only do something to do with worms
};


void fun()
{
    A ax;
    B bx;

    a.f(); // has to be the right thing
    b.f();
}