【问题标题】:General questions about std::variant关于 std::variant 的一般问题
【发布时间】:2020-03-21 15:05:46
【问题描述】:

我对新的 std::variant 类型有一些疑问。我考虑将它用作联合的类型安全替代方案。我写了一些应该在在线编译器中编译的测试代码。我还没有找到很多关于在 std::variant 中存储 std::array 等类型的信息。使用 std::variant 似乎无法进行原始缓冲区存储(例如 uint8_t [4])。

#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::variant 时是否存在明显的代码膨胀或开销?特别是因为我基本上使用的是 POD 类型(uint32_t 和 uint8_t[4] 的联合)。

非常感谢。

【问题讨论】:

  • Um get 从不返回副本。对于varianttuplestd::arraystd::get 被重载的任何其他类型,没有get 的形式返回相关对象的副本 .我不知道你是怎么想到get 复制了一些东西,尤其是当它就在函数的签名中时。
  • 我在 setComParams() 函数中使用 get 而不是 get_if 并且 main() 中的值没有改变。也许错误出在其他地方。
  • @Spacefish 您的代码运行良好并给出了您想要的输出:godbolt.org/z/CFvM9q
  • 好吧,你是对的。我在尝试 std::get_if 之前写了 auto paramArray = std::get ... 而不是 auto & paramArray = std::get 所以我认为这是错误的。

标签: c++ coding-style c++17 variant


【解决方案1】:

您可以使用您要放置的类型的纯右值直接分配/初始化std::variant

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,而不是std::get_ifstd::get 只会在您尝试访问错误类型并返回包含对象的 reference 时抛出异常。它也从不复制对象。:

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

std::variant 类型列表中不允许内置数组类型或引用类型或函数类型,因为类型需要满足可破坏的概念,即表达式

t.~T()

其中t 的类型为T 必须是格式良好的。这不适用于内置数组,但几乎适用于所有其他合理的对象类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-12
    相关资源
    最近更新 更多