Получить int из boost:: variant сгенерировать ошибку сегментации

Я пытаюсь получить значение int из boost::variant. Код генерирует ошибку сегментации - почему? Я добавляю комментарии в код, какие строки вызывают ошибку. Я предполагал, что

int numberInt = boost::get<int>(v);

не будет работать правильно, поэтому я изменил его на

int *ptrInt = boost::get<int>(&v);

который компилируется, но мне все же не удалось получить значение int? Точно так же и с двойным. Строковый тип работает.

#include <iostream>
#include "boost/variant.hpp"
#include <boost/variant/get.hpp>
using namespace std;

int main(int argc, char* argv[])
{
  boost::variant<int, double, std::string> v;
  v = 16;
  v = 3.1415;
  v = "hello new year";

  //int numberInt = boost::get<int>(v);     //1) not working
  //double numberDouble = boost::get<double>(v);//2) not working

  int *ptrInt = boost::get<int>(&v);        //3) compiling
  if(ptrInt) 
    cout << *ptrInt << endl;            //4) not displayed
  //cout << *ptrInt << endl;            //5) segmentation fault

  double *ptrDouble = boost::get<double>(&v);   //6) compiling
  if(ptrDouble) 
    cout << *ptrDouble << endl;         //7) not displayed
  //cout << *ptrDouble << endl;         //8) segmentation fault

  std::string caption = boost::get<string>(v);
  cout << caption << endl;          //9) working

  return 0;
}

// clear && clear && g++ test.cpp -std=c++11 -o test && ./test

person user2856064    schedule 03.01.2015    source источник


Ответы (2)


Я думаю, вы неправильно понимаете, что такое вариант повышения. В документации библиотеки тип variant описывается как «многотипный, одно значение». (выделено мной). Поскольку вы присвоили значение типа std::string, никакие другие типы значений не хранятся в файле variant. Хорошая вещь о variant (по сравнению с union) описана в комментариях к функции get:

// Retrieves content of given variant object if content is of type T.
// Otherwise: pointer ver. returns 0; reference ver. throws bad_get.

Таким образом, если int numberInt = boost::get<int>(v); работает правильно, должно возникнуть исключение. А int *ptrInt = boost::get<int>(&v); должен возвращать нулевой указатель. Разыменование нулевого указателя является неопределенным поведением и, вероятно, причиной ваших ошибок сегментации.

Я думаю, что поведение, которое вы ищете, находится в tuple (найдено как в boost, так и в std). Простая структура/класс тоже подойдет, если вы не против указать имя для объектов-членов.

person eerorika    schedule 03.01.2015

Боюсь, вы не поняли, как работает boost::variant. В теории типов boost::variant — это Sum Type, или Algebraic Data Type.

Это также часто называют «дискриминационным союзом» и в основном выглядит (в данном случае):

struct Variant {
    size_t index;
    union {
        int a;
        double b;
        std::string c;
    } u;
};

Теперь, когда вы пишете v = 16, происходит следующее:

v.u.a = 16; v.index = 0;

Когда вы пишете v = 3.1415, происходит следующее:

v.u.b = 3.1415; v.index = 1;

И, наконец, когда вы пишете v = "hello new year", происходит следующее:

v.u.c = "hello new year"; v.index = 2;

Обратите внимание, что каждый раз index, представляющий, какой член union активен в данный момент, обновляется... и, следовательно, в любой момент времени активен только один член союза.

Когда вы используете boost::get<int>(&v), код на самом деле выглядит так:

int* get_0(Variant* v) {
    if (v && v->index == 0) { return &v->u.a; }
    return nullptr;
}

и поэтому, поскольку в этот момент v->index равно 2, он возвращает nullptr.

Единственный get, который будет работать, — это boost::get<std::string>(&v), потому что он проверяет, является ли index 2, и поэтому возвращает указатель на v.u.c.

person Matthieu M.    schedule 03.01.2015