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;;
}
但这不是我想要达到的。
问题是 - 我可以使用提供的方法来获取值,而无需在我需要的每个代码位置指定类型吗?
环境信息:
- OS: Linux
- 编译器:g++ (GCC) 11.1.0
- 标准:C++17
在涉及任何类型的优化时,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)
可以帮到你。
我的目的是从数据数组中获取任何值,而不是每次获取值时都指定类型。我创建了描述字段信息(字段名称和类型)的特殊表格,还编写了一个函数来帮助我正确解释数据。
代码如下:
#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;;
}
但这不是我想要达到的。
问题是 - 我可以使用提供的方法来获取值,而无需在我需要的每个代码位置指定类型吗?
环境信息:
- OS: Linux
- 编译器:g++ (GCC) 11.1.0
- 标准:C++17
在涉及任何类型的优化时,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)
可以帮到你。