Создание идентичных псевдонимов типа С++ несовместимо
Я использую 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) ...
Ответы
Ответ 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();
}