Храните разные типы переменных в одной сущности в C

#c #unions

#c #объединения

Вопрос:

Я относительно новичок в C , поэтому пытаюсь найти наилучший способ хранения различных типов значений в одной сущности. Контекст заключается в том, что у меня есть функция, которая возвращает структуру, и одно из значений может быть разных типов. В любой момент времени будет установлено только одно значение. Лучшим вариантом для меня было бы, если бы я мог сделать что-то вроде этого:

 Union Union1 {
int x;
char y;
double z;
}

executor(Union1 union1) {
  if(union1.x.isSet()) {
    doSomethingUsingX();
  } else if(union1.y.isSet()) {
    doSomethingElseUsingY();
  }
}
  

Я не думаю, что это возможно, потому что объединения c используют одинаковое пространство памяти.
Итак, есть ли что-нибудь, что я могу использовать?

Ответ №1:

Если вы используете C 17 или новее, попробуйте std::variant . Смотрите https://en.cppreference.com/w/cpp/utility/variant

Если вы старше C 17, вы можете попробовать boost::variant

variant Это лучший способ выполнить объединение. В нем также записывается, что за объект в нем хранится, и вы можете использовать std::visit для вызова функции с перегруженным типом в variant .

Вот так, адаптировано из примера cppreference:

 #include <iostream>
#include <variant>
#include <vector>

// helper type for the visitor #4
template <class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
// explicit deduction guide (not needed as of C  20)
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;

int main() {
  std::vector<std::variant<int, char, double>> v;
  v.emplace_back(1);
  v.emplace_back('c');
  v.emplace_back(2.7);

  for (const auto amp;x : v) {
    std::visit(
        overloaded{[](auto arg) { std::cout << "unknown " << arg << 'n'; },
                   [](int arg) { std::cout << "int " << arg << 'n'; },
                   [](double arg) { std::cout << "double " << arg << 'n'; },
                   [](char arg) { std::cout << "char " << arg << 'n'; }},
        x);
  }
  return 0;
}
  

Также этот пост в блоге, который я нашел в Интернете, кажется, помогает объяснить, как это работает:
https://pabloariasal.github.io/2018/06/26/std-variant /

Ответ №2:

Вы могли бы использовать перечисление для сообщения возвращаемого типа:

 typedef enum
{
    x,
    y,
    z
}returnType;

typedef struct
{
    returnType rt;
    union
    {
        int x;
        char y;
        double z;
    };
}myStruct;

//...
myStruct AFunctionWithMyStructReturnType(int arg)
{
    myStruct ms;
    if(arg == 0)
    {
        ms.rt = returnType.x;
        ms.val = 10000;
    }
    else if(arg == 1)
    {
        ms.rt = returnType.y;
        ms.val = 200;
    }
    else
    {
        ms.rt = returnType.z;
        ms.val = 3.14;
    }
    return ms;
}
//...

myStruct S = AFunctionWithMyStructReturnType(arg);
switch(S.rt)
{
    case returnType.x:
        processX(S.val);
        break;

    case returnType.y:
        processY(S.val);
        break;

    case returnType.z:
        processZ(S.val);
        break;
}
  

Комментарии:

1. Проблема с объединением в том, что вы не можете хранить в них типы STL, подобные std::vector , поскольку нет способа узнать, как уничтожить такие объекты. std::variant упоминание в другом ответе — лучший выбор, если вы используете C 17/