问题描述
我的目的是从数据数组中获取任何值,而不在每次获取值时指定类型。我创建了描述字段信息(字段名称和类型)的特殊表格,并编写了一个函数来帮助我正确解释数据。
代码如下:
#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",};
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;
}
我希望编译器能正确推导出返回值并让我进行数字比较。但是,我收到了以下编译器消息:
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;;
}
但这不是我想要的。
问题是 - 我是否可以使用所提供的方法来获取值,而无需在我需要的每个代码位置指定类型?
环境信息:
- 操作系统:Linux
- 编译器:g++ (GCC) 11.1.0
- 标准:C++17
解决方法
当涉及到任何类型的优化时,C++ 的一个基本规则是任何编译器优化都不能产生任何“可观察到的效果”。
这意味着,除其他外,格式良好的代码无法优化为格式错误的代码。并且格式错误的代码无法优化为格式良好的代码。
AttributeEditor
const auto value = getValue(ValueType::Uint32,arr,length);
被声明为返回一个 getValue()
。这就是它的返回类型,这就是 std::variant
的类型被推导出来的。 value
是声明的 value
。句号。它实际上是否可以具有任何特定值对于确定代码是否格式良好并不重要。因此,后面的 std::variant
必须对所有可能的变体值都是良构的,而且您已经知道它不适用于其中之一。
好了,故事到这里就差不多结束了。显示的代码格式错误并且不是有效的 C++。确实,在显示的代码中,std::visit
不会返回包含后续 getValue()
格式错误的值的变体。然而,由于上述原因,这并不重要。
以上所有内容还意味着:如果声明的变体不能包含 std::visit
格式错误的值,或者访问者的代码被调整为格式正确,那么:如果编译器可以推导出这里返回的实际类型,编译器可以(但不需要)完全优化而不首先构造变体,只生成返回唯一可能值的代码,并访问每个直接值。这是因为消除变体的构建/破坏将不会产生可观察到的影响。
您可以使用“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)
可以帮到你。