Объявление типа данных динамически в C++
Я хочу иметь возможность сделать следующее:
У меня есть массив строк, которые содержат типы данных:
string DataTypeValues[20] = {"char", "unsigned char", "short", "int"};
Позже я хотел бы создать переменную одного из типов данных во время выполнения. Я не буду знать во время компиляции, какой должен быть правильный тип данных.
Так, например, если во время выполнения я определил, что переменная x должна иметь тип int:
DataTypeValues[3] x = 100;
Очевидно, это не сработает, так как я могу сделать что-то подобное?
14 ответов
Простой ответ заключается в том, что вы не можете - типы должны быть известны во время компиляции в C++. Вы можете сделать что-то подобное, используя такие вещи, как boost::any или союзы, но это не будет красиво.
Вам нужно было бы использовать объединения для достижения чего-то подобного, но обработка союзов - это очень сложный вопрос, поэтому вы должны выбрать контейнерный класс, который оборачивает логику объединения за интерфейс, такой как Boost.Variant или Qts QVariant.
Ты не можешь Этот вид метапрограммирования во время выполнения не поддерживается в C++.
Всем, кто говорит, что вы не можете сделать это в C++, не хватает одного очевидного решения. Здесь вы можете использовать базовый класс, вам нужно определить обычно используемый интерфейс, и тогда все производные классы будут теми типами, которые вам нужны. Поместите его в смарт-указатель, соответствующий контейнеру, и все готово. Возможно, вам придется использовать динамический вывод типа, если вы не можете поместить достаточно интерфейса в базовый класс, который всегда вызывает недовольство, потому что это некрасиво, но оно есть по причине. А динамическое размещение типов, вероятно, не самая эффективная вещь, но, как всегда, это зависит от того, для чего вы ее используете.
Используйте union и создайте свой собственный динамический класс. псевдокод, такой как:
union all{
char c;
unsigned char uc;
short s;
int i;
};
class dynamic{
public:
char Type;
all x;
template <class T>
dynamic(char type_,T y){
int Int;
char Char;
unsigned char Uchar;
short Short;
if(typeof(y) ==typeof(Char)){
Type=1;
}else if(typeof(y) ==typeof(Uchar)) {
Type=2;
}else if(typeof(y) ==typeof(Short)) {
Type 3;
}else{
Type=4;
}
switch (Type) {
case 1: x.c=y; break;
case 2: x.uc=y; break;
case 3: x.s=y; break ;
case 4: x.i=y; break ;
}
}
auto get() {
switch(Type) {
case 1: return x.c;
case 2: return x.uc;
case 3: return x.s;
case 4: retuen x.i;
}
}
//make also the operators function you like
} ;
однако следует избегать использования динамического типа, насколько это возможно, потому что он неэффективен
(в этом примере каждый динамический объект будет занимать 5 байтов)
это также замедлит ваш код (немного).
если в вашем примере вы хотите использовать динамический тип числовой переменной только для уменьшения использования памяти, вам следует забыть о динамическом и просто использовать целое число в качестве типа (где целое число может содержать сразу все символы char, unsigned char и short).
но если вы хотите использовать его, потому что вам нужен динамический тип между чем-то действительно другим (например, между int и строкой или пользовательским объектом), тогда это будет один из ваших вариантов.
Самое близкое, что вы можете получить - это шаблоны:
template<int i> class Data { };
template<> class Data<0> { typedef char type; }
template<> class Data<1> { typedef unsigned char type; }
template<> class Data<2 { typedef short type; }
template<> class Data<3> { typedef int type; }
Data<3>::Type x;
Если вам нужно что-то более сложное, Boost имеет мост C++-Python.
Я думаю, что вы действительно ищете динамически типизированный язык. Вставьте интерпретатор, если вы должны придерживаться C++!
Или вы можете реализовать что-то похожее на модель компонентов, используя интерфейсы для работы с упакованными данными. Начните с космического базового класса - IObject, затем реализуйте интерфейсы для IInteger, IDouble, IString и т. Д. Затем сами объекты будут создаваться фабрикой.
Или вы можете просто использовать пустые буферы с фабрикой... Это старый способ избежать статической типизации в C/C++ (без использования полиморфизма на основе наследования). Затем посыпьте щедрым количеством reinterpret_cast.
Я думаю, что этот ответ будет на несколько лет позже. Но для людей, которые могут увидеть эту ветку, возможным решением будет использование переменных шаблонов. Например:
template<typename T>
T var;
template<typename T>
T arr[10];
int main() {
int temp;
var<int> = 2;
cout << var<int> << ' '; // this would output 2
var<char> = 'a';
cout << var<int> << ' '; // var<int> value would be a space character
cout << var<char> << ' '; // this would output 'a'
for(int i = 0; i < 10; i++) {
switch(i % 2) {
case 0:
arr<int>[i] = ++temp;
break;
case 1:
arr<char>[i] = 'a' + ++temp;
break;
}
cout << endl;
for(int i = 0; i < 10; i++) {
switch(i % 2) {
case 0:
cout << arr<int>[i] << ' ';
break;
case 1:
cout << arr<char>[i] << ' ';
break;
}
}
return 0;
}
Единственная проблема заключается в том, что вам необходимо знать тип переменной того, что в данный момент находится внутри переменной (например, сохранение в целочисленном массиве того, что является "идентификатором" переменной (идентификатором, который вы ей дадите) для определенного типа)., Если вы не знаете или не имеете условия, чтобы узнать, что находится внутри определенной переменной или расположения массива, я не рекомендую использовать это.
Единственный способ, который приходит на ум сейчас, - это старый стиль C, в котором указатель на void использовался следующим образом:
void *unkown;
Обозначим, что вы можете назначить ему любой объект, как показано ниже:
unkown = (void *)new int(4);
Если вы знаете тип во время выполнения, вы можете запустить указанную функцию для такой переменной, как показано ниже:
if(type==0) { // int
printf("%d\n", * ((int*)unkown) );
} else {
// other type
}
Этот способ (приведение к пустоте *) используется, например, когда используется функция malloc [и т. Д.].
Я не говорю, что это хорошая практика, когда с ++ сейчас гораздо более развит. Все еще соглашайтесь с людьми, которые говорят, что это не лучшее решение для вашей проблемы. Но, возможно, после некоторой переделки вы можете найти это полезным.
Вы также можете найти интересный автоматический тип начиная с C++ 11. http://en.cppreference.com/w/cpp/language/auto
Единственное, что вы можете сделать, - это вручную просмотреть типы и сравнить каждый из них. Здесь также есть возможность использовать объект фабрики - но это может включать кучу.
В вашем простом примере было бы мало пользы от того, чтобы не просто использовать самый широкий тип в списке в качестве универсального контейнера и приводить к меньшим типам при необходимости (или даже полагаться на неявное приведение).
Вы можете разобраться с объединениями, классами, полиморфизмом, RTTI, вариантами Boost и т. Д., Но просто для списка целых чисел различной ширины это вряд ли стоит усилий.
Мне кажется, у вас есть осознанная проблема, для которой вы придумали непрактичное решение, к которому вы сейчас обращаетесь за помощью. Возможно, вам лучше было бы описать вашу первоначальную проблему, а не решать ее!
Тип данных Variant в Visual Basic - это то, о чем вы говорите. Он может содержать что угодно, первичные типы данных, массивы, объекты и т. Д.
"Класс Collection в OLE Automation может хранить элементы разных типов данных. Поскольку тип данных этих элементов не может быть известен во время компиляции, методы добавления элементов в коллекцию и извлечения элементов из нее используют варианты. Если в Visual Basic для Используется каждая конструкция, переменная итератора должна иметь тип объекта или вариант." - с http://en.wikipedia.org/wiki/Variant_type
Вышеуказанная страница дает некоторое представление о том, как используются варианты, и показывает, как OLE используется в C++ для работы с вариантами.
Также не забывайте, что все функции, которые должны работать с этим загадочным типом данных. Большинство функций предназначены для использования только одного типа, например, сложение. Функции перегружены для обработки дополнительных типов.
Как вы узнаете во время выполнения, что тип переменной?
Я пытаюсь опубликовать это здесь, но у меня была ошибка формата. Решил поставить ссылку. В любом случае вы можете использовать (long long) для хранения адресов, потому что размер адреса равен 8, а размер (long long) также равен 8, тогда он может содержать адрес.
https://www.flatech.com.au/learning-material/programming/c/object-pointers-to-any-type