Как вы, наверное, знаете, что typedef больше похож на псевдоним в С++, а не на новый тип, подробности можно увидеть здесь:
http://dlang.org/cpptod.html#typedefs
Мне действительно не нравится решение, предложенное в ссылке, поэтому мне интересно, есть ли лучший способ?
Есть ли способ имитации "strongdef"?
Ответ 1
Существует только один способ ввести новый тип в С++ - с ключевым словом класса (union
/struct
/class
/enum
...). Есть способы скрыть этот материал за макросами и сделать эти новые типы интуитивно понятными, как старые (см. BOOST_STRONG_TYPEDEF
), но факт остается, что это единственный способ ввести этот новый тип.
Представьте, что у вас есть ключевое слово newtype
, которое делает сильный typedef:
newtype foo = int; // with 'using' alias syntax
Как будут выполняться преобразования и из этого типа? Без конверсий вы никогда не сможете присвоить какое-либо значение объекту вашего нового типа. Только явные преобразования могут показаться интуитивными, но что делать, если вы действительно хотите неявных преобразований и все еще можете перегружать? Ну, тяжелая удача. Вы могли бы добавить все виды синтаксиса, чтобы оставить решение для пользователя, но я уверен, что вы всегда можете придумать угловые случаи, которые потребуют нового синтаксиса. Просто сделайте это struct
, спрячьте его за макросом и сделайте с ним.
Ответ 2
Общий "время для создания правильного интерфейса":
#define STRONG_CLASS_TYPEDEF(oldType, newType) \
struct newType : private oldType { \
template<typename... T> \
newType(T... foo) : oldType(foo...) {} \
oldType* operator->() { return this; } \
oldType& operator*() {return *this;} \
};
Если ваш компилятор не поддерживает вариативные шаблоны, вам нужно вручную вставить любые необходимые вам конструкторы. Это в основном собственный конструктор наследования, построенный на свой собственный.
К сожалению, я не думаю, что можно ограничить новый тип неявным кастингом на базу, но выставить все его публичные поля. Существует несколько способов:
Вы можете просто using oldType::X;
использовать все методы и переменные, которые вам нужны. Это, безусловно, лучшее решение, но требуется некоторое время.
Или используйте перегрузку оператора скрытой стрелки и вызовите foo->method();
. Или "разыгрывать" новый сильный тип в базовый тип. Это в основном просто причудливый явный актерский состав. Но, учитывая operator oldType()
(явный или нет), даже не работает с частным наследованием...
В любом случае, здесь сильный тип для примера std::string
и Name
.
struct Name : private std::string {
template<typename... T>
Name(T... foo) : std::string(foo...) {}
std::string* operator->() { return this; }
const std::string& operator*() {return *this;}
//The above is obviously a bad idea if you want to use this alongside pointers
using std::string::resize;
using std::string::size;
using std::string::insert;
using std::string::operator[];
// etc.
//Simplest to use, but a bit more to set up
};
Кроме того, вам придется обернуть перегрузку операторов, отличных от членов (std::string ==
в этом примере).
bool operator==(Name& lhs, Name& rhs) { return *lhs == *rhs; }
//Note: "Dereference" turns it into a std::string, as above
Справедливое предупреждение, я не испытал многого за пределами основной механики. Это может добавить (очень) небольшое количество накладных расходов. Но фактически пустой класс, вероятно, оптимизирован.
Ответ 3
Используйте BOOST_STRONG_TYPEDEF
для создания "strongdefs" на С++. Для этого нет встроенной языковой функции.
Было бы интересно узнать реальную проблему, которую вы пытаетесь решить, потому что я нашел необходимость в том, чтобы не-псевдоним typedef
был очень минимальным для кода, над которым я работал.
Ответ 4
Новые типы на С++ могут приводить только к каким-то агрегатным подтипам. Но поскольку перегрузка функций основана на статическом типе, все операции и функции должны быть обновлены.
С++ typedef
похожи на D alias
es.
В случае, если начальный тип является классом или структурой, наследование может каким-то образом помочь (по крайней мере, операции могут работать как со старыми параметрами), но все идет о типе возврата, и преобразование должно быть переопределено должным образом, в противном случае все преобразуется равномерно вверх и вниз, тем самым исчезая преимущество нового типа.
Ответ 5
В С++ существует множество способов выполнения проверки типов. Рассмотрим следующий код: основная идея такая же, как в примере, приведенном в ссылке, но я думаю, что это более просто:
struct Handle2 {void * ptr;} ;
void bar(Handle2) {}
void foo(void *) {}
int main() {
Handle2 h2 = {(void*)-1};
foo(h2); //! won't pass through compiling
bar(h2);
}