Общие вопросы о STD :: вариант - PullRequest
0 голосов
/ 21 марта 2020

У меня возникли вопросы по поводу нового типа std :: option. Я подумал об использовании его в качестве безопасной альтернативы союзу. Я написал тестовый код, который должен компилироваться в онлайн-компиляторе. Я не нашел много информации относительно хранения типов, таких как std :: array в std :: варианте. Необработанное буферное хранилище (например, uint8_t [4]) не представляется возможным с помощью std :: Вариант.

#include <iostream>
#include <array>
#include <variant>
#include <array>
#include <stdint.h>
#include <typeinfo>
#include <cstdlib>
using parameters_t = std::variant<uint32_t, std::array<uint8_t, 4>>;

void setComParams(parameters_t& comParameters_, uint8_t value);

int main()
{
    std::array<uint8_t, 4> test;
    parameters_t comParameters = test; // can I assign the array here directly in some way?
    auto parametersArray = std::get_if<std::array<uint8_t, 4>>(&comParameters);
    if(parametersArray == nullptr) {
        exit(0);
    }

    auto & parameterReference1 = *parametersArray; //so I can use [] indexing
    parameterReference1[1] = 5;
    parameterReference1[3] = 6;
    std::cout << "I should be 5: " << (int)(*parametersArray)[1] << std::endl;
    std::cout << "I should be 5: " << (int)parameterReference1[1] << std::endl;
    std::cout << "I should be 6: " << (int)(*parametersArray)[3] << std::endl;
    std::cout << "I should be 6: " << (int)parameterReference1[3] << std::endl;
    setComParams(comParameters, 10);
    std::cout << "I should be 10: "<< (int)(*parametersArray)[1] << std::endl;
    std::cout << "I should be 10: "<< (int)parameterReference1[1] << std::endl;

    comParameters = 16; // this should be an uint32_t now
    auto parametersArray2 = std::get_if<std::array<uint8_t, 4>>(&comParameters);
    if(parametersArray2 == nullptr) {
        std::cout << "Everything in order" << std::endl;
    }

    uint32_t * parameterNumber = std::get_if<0>(&comParameters); // using index now
    *parameterNumber = 20;
    std::cout << "I should be 20: "<< (int)*parameterNumber << std::endl;
    setComParams(comParameters,30);
    std::cout << "I should be 30: "<< (int)*parameterNumber << std::endl;
    return 0;
}

void setComParams(parameters_t &comParameters_, uint8_t value) {
    auto comParametersArray = std::get_if<std::array<uint8_t, 4>>(&comParameters_);
    if(comParametersArray == nullptr) {
        auto comParameterValue = std::get_if<uint32_t>(&comParameters_);
        if(comParameterValue != nullptr) {
            *comParameterValue = value;
        }
    }
    else {
        auto & arrayReference = *comParametersArray;
        arrayReference[1] = value;
    }
}

Просто чтобы убедиться, что я все правильно понял: если я использую std :: get, я всегда получить копию, что означает, что независимо от того, как я это делаю, я не могу изменить фактическое значение варианта. Всегда ли мне нужно использовать std :: get_if в этом случае? Кроме того, есть ли существенное увеличение или увеличение объема кода при использовании std :: варианта по сравнению с объединениями? Тем более, что я в основном использую POD-типы (объединение uint32_t и uint8_t [4]).

Заранее большое спасибо.

1 Ответ

2 голосов
/ 21 марта 2020

Вы можете назначить / инициализировать std::variant напрямую с помощью prvalue типа, который вы хотите добавить:

parameters_t comParameters = std::array<uint8_t, 4>{};

, хотя это инициализирует ноль массива в отличие от вашего кода.

Если вы хотите избежать конструкции копирования / перемещения, связанной с этим, вы можете использовать

parameters_t comParameters(std::in_place_type<std::array<uint8_t, 4>>);

для инициализации или

comParameters.emplace<std::array<uint8_t, 4>>();

для назначения.

Оба все еще обнуляют инициализацию std::array. Насколько я знаю, в настоящее время нет способа инициализировать объект по умолчанию в std::variant.

std::get_if возвращает указатель на объект в std::variant , Он никогда не делает копию объекта.

Вместо std::get_if вы также можете использовать std::get. std::get просто выдаст исключение, если вы попытаетесь получить доступ к неправильному типу и вернет ссылку на содержащийся объект. Он также никогда не копирует объект .:

auto& parameterReference1 = std::get<std::array<uint8_t, 4>>(comParameters);

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

t.~T()

, где t имеет тип T, должно быть правильно сформировано. Это не относится к встроенным массивам, но почти ко всем другим типам разумных объектов.

...