Подтвердить что ты не робот

Создание идентичных псевдонимов типа С++ несовместимо

Я использую 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. Очевидно, что A и B идентичны.

Каков самый простой способ правильно выполнить этот код:

fa(ax);
fb(bx);

и выполните этот код:

fa(bx);
fb(ax);

Конечно, я могу обернуть std::vector<int> в другой класс, но тогда мне нужно будет переписать его интерфейс. В качестве альтернативы я мог бы наследовать от std::vector<int>, но это часто обескураживается.

Короче говоря, мне нужны две несовместимые версии std::vector<int>.

ИЗМЕНИТЬ

Было высказано предположение, что Сильные 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) ...
4b9b3361

Ответ 1

Я думаю, что все, что вам нужно, по-прежнему лучше всего:

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

Он делает именно то, что вы хотите. Нет причин придумывать какой-то уродливый хакер, чтобы избежать чистого заявления. Основная причина, по которой я вижу, что такое подтипирование не благоприятствует, заключается в том, что одни и те же вещи должны вести себя одинаково и могут использоваться взаимозаменяемо. Но это именно то, что вы хотите подавить, и поэтому подтипирование делает именно то, что вы хотите: они имеют один и тот же интерфейс, но они не должны использоваться одинаково, потому что они не совпадают.

Ответ 2

Так или иначе, это случай примитивной одержимости. Либо int действительно что-то представляет, а vector представляет собой коллекцию этого, или vector<int> что-то представляет.

В обоих случаях это должно быть разрешено путем переноса примитива в нечто более значимое. Например:

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

std::vector<row> и std::vector<column> не будут взаимозаменяемыми.

Конечно, та же идея может быть применена к vector<int> вместо int, если vector<int> - это примитив, который действительно означает что-то еще.

Ответ 3

В качестве альтернативы я мог бы наследовать от std::vector, но это часто обескураживает.

ИМО, это зависит от ситуации. В общем, может быть хорошим решением

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

Вы все равно можете использовать VectorA и VectorB как обычный вектор, но вы не можете переключаться между ними.

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

Ответ 4

Это отчасти потому, что вы можете делать объектно-ориентированное программирование на С++, а также объектно-ориентированное программирование, повторно использующее типы библиотек.

Создайте классы A и B, которые моделируют поведение в вашем домене. Не имеет значения, случается ли, что оба поведения реализованы с полями, которые являются векторами ints; пока вы не нарушаете инкапсуляцию, все операции над отдельными векторами будут находиться в сфере их класса, и никакая путаница не может произойти.

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