std::visit 无法推断出 std::variant 的类型

std::visit can't deduce type of std::variant

我的目的是从数据数组中获取任何值,而不是每次获取值时都指定类型。我创建了描述字段信息(字段名称和类型)的特殊表格,还编写了一个函数来帮助我正确解释数据。

代码如下:

#include <iostream>
#include <variant>
#include <assert.h>
#include <string_view>
#include <unordered_map>

enum class ValueType : uint8_t
{
    Undefined       = 0x00,
    Uint32,
    AsciiString,
};

typedef uint64_t FieldId;

struct FieldInfo
{
    std::string _name;
    ValueType _type;
};

typedef std::unordered_map<FieldId, FieldInfo> FieldContainer;

static FieldContainer requestFields =
{
    { 0, { "user-id",   ValueType::Uint32, } },
    { 1, { "group-id",  ValueType::Uint32, } },
};

std::variant<uint8_t, uint32_t, std::string_view> getValue(ValueType type,
                                                           const uint8_t* data,
                                                           size_t length)
{
    if (type == ValueType::Uint32)
    {
        assert(length == sizeof(uint32_t));
        return *reinterpret_cast<const uint32_t*>(data);
    }
    else if (type == ValueType::AsciiString)
    {
        return std::string_view(reinterpret_cast<const char*>(data), length);
    }

    return static_cast<uint8_t>(0);
}


int main(int argc, char *argv[])
{
    const uint8_t arr[] = {0x00, 0x11, 0x22, 0x33};
    size_t length = sizeof(arr);

    const auto value = getValue(ValueType::Uint32, arr, length);

    std::visit([](auto&& arg)
                    {
                        if ( arg == 0x33221100 )
                        {
                            std::cout << "Value has been found" << std::endl;
                        }
                    }, value);
    return 0;
}

我希望编译器能够正确地推断出 return 值并让我进行数字比较。但是,我收到以下编译器消息:

error: no match for ‘operator==’ (operand types are ‘const std::basic_string_view<char>’ and ‘int’)
   57 |                         if ( arg == 0x33221100 )
      |                              ~~~~^~~~~~~~~~~~~

error: invalid conversion from ‘int’ to ‘const char*’ [-fpermissive]
   57 |                         if ( arg == 0x33221100 )
      |                                     ^~~~~~~~~~
      |                                     |
      |                                     int

我知道我可以通过调用 std::get:

获得价值
    if ( std::get<uint32_t>(value) == 0x33221100 )
    {
        std::cout << "Value has been found" << std::endl;;
    }

但这不是我想要达到的。

问题是 - 我可以使用提供的方法来获取值,而无需在我需要的每个代码位置指定类型吗?

环境信息:

在涉及任何类型的优化时,C++ 的一个基本规则是任何编译器优化都不会产生任何“可观察到的效果”。

除其他外,这意味着不能将格式良好的代码优化为格式错误的代码。并且格式错误的代码无法优化为格式正确的代码。

const auto value = getValue(ValueType::Uint32, arr, length);

getValue() 声明为 returning a std::variant。这就是 return 类型的含义,这就是 value 类型的推导结果。 value 是声明的 std::variant。句号。就确定代码是否格式良好而言,它实际上是否可以具有任何特定值并不重要。因此,随后的 std::visit 对于所有可能的变体值都必须是格式正确的,并且您已经了解它不适用于其中之一。

好吧,故事差不多就这样结束了。显示的代码格式错误且不是有效的 C++。的确,在显示的代码中 getValue() 没有 return 一个变体持有一个值,而随后的 std::visit 是格式错误的。然而,由于所述原因,这并不重要。

以上所有内容也意味着以下内容:如果声明的变体不能保存 std::visit 格式错误的值,或者调整访问者的代码使其格式正确,则:如果编译器可以推断出此处 return 的实际类型,则编译器可能(但不需要)完全优化掉而不是首先构造变体,而只生成 returns 是唯一可能的 value/values,并直接访问每个值。这是因为消除变体的 construction/destruction 不会产生明显的影响。

您可以使用“if constexpr”来解决 Igor Tandetnik 提到的问题。如果你可以使用 c++20,你可以使用结合“if constexpr”的概念来对类型进行分组并处理它们,这样你就不必为每种类型都写一个 if。

示例:

#include <cstdint>
#include <iostream>
#include <type_traits>
#include <variant>
struct StructA
{
};

struct StructWithCoolFunction
{
  void
  coolFunction ()
  {
  }
};

struct AnotherStructWithCoolFunction
{
  void
  coolFunction ()
  {
  }
};

struct UnhandledStruct
{
};

typedef std::variant<StructA, StructWithCoolFunction, AnotherStructWithCoolFunction, UnhandledStruct> MyVariant;
template <typename T> concept HasCoolFunction = requires(T t) { t.coolFunction (); };

auto const handleVariant = [] (auto &&arg) {
  using ArgType = typename std::remove_cv<typename std::remove_reference<decltype (arg)>::type>::type;
  if constexpr (std::is_same<StructA, ArgType>::value)
    {
      std::cout << "ArgType == StructA" << std::endl;
    }
  else if constexpr (HasCoolFunction<ArgType>)
    {
      std::cout << "some struct with a function 'coolFunction ()'" << std::endl;
    }
  else
    {
      std::cout << "not handled type" << std::endl;
    }
};

int
main ()
{
  auto variantWithStructA = MyVariant{ StructA{} };
  std::visit (handleVariant, variantWithStructA);
  auto variantWithStructWithCoolFunction = MyVariant{ StructWithCoolFunction{} };
  std::visit (handleVariant, variantWithStructWithCoolFunction);
  std::visit (handleVariant, MyVariant{ UnhandledStruct{} });
  return 0;
}

如果您没有 c++20,您也可以尝试使用 type_traits pre c++20 来寻找解决方案:

if constexpr(std::is_integral<ArgType>::value) 

可以帮到你。