简体   繁体   English

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

[英]Making identical C++ type aliases incompatible

I use std::vector<int> for two different kinds of information. 我使用std::vector<int>来获取两种不同的信息。 I want to be sure that I don't accidentally mix the two uses. 我想确保我不会意外地混合这两种用途。

In short, I want something like this piece of code to fail: 简而言之,我想要像这段代码一样失败:

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

This code compiles, even though fa expects an argument of type A . 即使fa期望类型为A的参数,此代码也会编译。 Obviously, A and B are identical. 显然, AB是相同的。

What is the simplest way to make this code compile correctly: 使这段代码正确编译的最简单方法是什么:

fa(ax);
fb(bx);

and make this code fail: 并使此代码失败:

fa(bx);
fb(ax);

Of course, I can wrap std::vector<int> within another class, but then I'll need to rewrite its interface. 当然,我可以将std::vector<int>包装在另一个类中,但是我需要重写它的接口。 Alternatively, I could inherit from std::vector<int> , but this is frequently discouraged. 或者,我可以从std::vector<int>继承,但经常不鼓励这样做。

In short, I need two incompatible versions of std::vector<int> . 简而言之,我需要两个不兼容的std::vector<int>

EDIT 编辑

It has been suggested that Strong typedefs can solve this problem. 有人建议Strong typedef可以解决这个问题。 This is only partially true. 这只是部分正确。 If I use BOOST_STRONG_TYPEDEF(std::vector<int>, A) , I need to add some annoying casts. 如果我使用BOOST_STRONG_TYPEDEF(std::vector<int>, A) ,我需要添加一些讨厌的强制转换。 For example, instead of 例如,而不是

A ax{1,3,5};

I need to use 我需要用

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

And instead of 而不是

for (auto x : ax) ...

I need to use 我需要用

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

I think what you want is still best achieved with: 我认为你想要的仍然是最好的:

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

It does exactly what you want. 它完全符合你的要求。 There's no reason to come up with some ugly hackery just to avoid a clean statement. 为了避免干净的陈述,没有理由想出一些丑陋的hackery。 The main reason I see that such subtyping is not favored is that the same things should behave like they are the same and can be used interchangeably. 我认为这种子类型不受欢迎的主要原因是相同的事物应该表现得像它们是相同的并且可以互换使用。 But that is exactly what you want to suppress, and therefore subtyping it makes exactly the statement that you want: they have the same interface but they shouldn't be used the same because they aren't the same. 但这正是你想要压缩的东西,因此它的子类型恰好就是你想要的语句:它们具有相同的接口,但它们不应该被使用,因为它们不相同。

One way or another, this is a case of primitive obsession . 无论如何,这是一种原始的痴迷 Either the int s really represent something and the vector s are a collection of that something, or the vector<int> s represent something. int s确实代表了某些东西,而vector s是那个东西的集合,或者vector<int>代表了某种东西。

In both cases, this should be solved by wrapping the primitive up into something more meaningful. 在这两种情况下,都应该通过将原语包装成更有意义的东西来解决。 For example: 例如:

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

std::vector<row> and std::vector<column> would not be interchangeable. std::vector<row>std::vector<column>不可互换。

Of course, the same idea could be applied to vector<int> instead of int , if vector<int> is the primitive that really means something else. 当然,如果vector<int>是真正意味着其他东西的原语,那么同样的想法可以应用于vector<int>而不是int

Alternatively, I could inherit from std::vector, but this is frequently discouraged. 或者,我可以从std :: vector继承,但这通常是不鼓励的。

IMO, it depends on the situation. IMO,这取决于具体情况。 In general could be a good solution 一般来说可能是一个很好的解决方案

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

You can still use VectorA and VectorB as normal vector, but you cannot switch among them. 您仍然可以使用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;
}

This is partly why you can do object oriented programming in C++ as well as object based programming reusing the library types. 这就是为什么你可以在C ++中进行面向对象编程以及重用库类型的基于对象的编程的部分原因。

Make A and B classes which model the behaviour in your domain. 制作A和B类,为您的域中的行为建模。 It doesn't matter if it happens that both behaviours are implemented with fields which are vectors of ints; 如果两个行为都是使用作为int的向量的字段实现的,那么这并不重要; as long as you do no not break encapsulation, all the operations on the distinct vectors will be in the scope of their class and no confusion can occur. 只要你不打破封装,对不同向量的所有操作都将在它们的类的范围内,并且不会发生混淆。

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

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

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