可以使用 std::any_cast 有效地转换 std::any
It is possible to efficiently cast std::any with std::any_cast
我正在尝试在我的图书馆中使用 std::any
。我以 cppreference.com 为例,并以这种方式实现示例:
#include <any>
#include <iostream>
int main()
{
struct example_1{
int i1;
int i2;
};
// any type
std::any a = 1;
std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
a = 3.14;
std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
a = true;
std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';
struct example_1 ex1;
ex1.i1 = 1;
ex1.i2 = 2;
a = ex1;
std::cout << a.type().name() << ": " << std::any_cast<struct example_1>(a).i1 << " ; " << std::any_cast<struct example_1>(a).i2 << '\n';
// bad cast
try
{
a = 1;
std::cout << std::any_cast<float>(a) << '\n';
}
catch (const std::bad_any_cast& e)
{
std::cout << e.what() << '\n';
}
// has value
a = 1;
if (a.has_value())
{
std::cout << a.type().name() << '\n';
}
// reset
a.reset();
if (!a.has_value())
{
std::cout << "no value\n";
}
// pointer to contained data
a = 1;
int* i = std::any_cast<int>(&a);
std::cout << *i << "\n";
}
输出如下:
i: 1
d: 3.14
b: 1
Z4mainE9example_1: 1 ; 2
bad any_cast
i
no value
1
在基本类型的情况下很容易,但在结构的情况下,我们有 return 调用 std::any.type().name()
结构的符号。
我认为要将传递的 std::any
参数转换为正确的类型,我可以在符号字符串中搜索是否包含子字符串 "structname" 或 "classname"。但我认为这只是一种解决方法。有没有办法有效地将 std::any
转换为正确的类型?
另一个问题,如果我在我的库中使用它,我不知道传递的对象的结构名称或 class(因此我的解决方法不起作用)。有没有办法检索它并使用 std::any_cast
正确的类型?
这是因为将在编译期间进行优化。如果你想在 runtime
上获得一个类型的全名,你必须 demangle
类型的名称(这不是标准的)。
假设您的类型名称在字符串变量中 type_name
:
int demangle_status;
char* demangled_name = abi::__cxa_demangle(type_name.c_str(), nullptr, nullptr, &demangle_status;
if (demangle_status == 0)
{
type_name = demangled_name;
std::free(demangled_name)
}
std::cout << type_name
请注意,您必须包含 <cxxabi.h>
才能调用 abi::__cxa_demangle
.type().name()
返回的值完全由实现定义,您无法以任何方式保证它们。您甚至无法保证 .type().name()
对于多个不同类型是不同的。
即使情况并非如此.type().name()
returns 一个运行时值并且您不能使用非常量值来计算类型。
C++ 是静态类型的。每个表达式(在模板实例化之后)都有一个在编译时固定的类型。因此,如果不了解它(可能)持有的类型,您就无法使用 std::any
。
您可以使用 any_cast
或 .type()
返回的 std::type_info
获取 std::any
持有的类型和值(通过它在.name()
),只有当你有一组有限的可能类型时,在编译时确定,它可以保存在该代码位置(在模板实例化之后)。如果您不知道它在您的代码中的给定位置(在模板实例化之后)的潜在类型,那么您将无法获得它在那里的类型或值。
如果您的库的用户打算将他们的类型与您的 std::any
一起使用,而您实际上想提取库代码中的值,那么您将需要模板化您的代码,而用户将需要提供潜在类型作为模板参数。
很少有使用 std::any
的情况。通常 std::variant
或虚拟基础 class 接口(用户可以从中派生出自己的类型)更可能是合适的工具。或者,如果实际上不需要运行时多态性(我们不知道您的用例),只需编写用户可以提供其自定义类型的模板代码就足够了。
另请注意:不要在 class 定义或显式前向声明之外的 C++ 类型名称前使用 struct
。这不是必需的,但如果类型的名称查找失败,可能会产生意想不到的后果。
我正在尝试在我的图书馆中使用 std::any
。我以 cppreference.com 为例,并以这种方式实现示例:
#include <any>
#include <iostream>
int main()
{
struct example_1{
int i1;
int i2;
};
// any type
std::any a = 1;
std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
a = 3.14;
std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
a = true;
std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';
struct example_1 ex1;
ex1.i1 = 1;
ex1.i2 = 2;
a = ex1;
std::cout << a.type().name() << ": " << std::any_cast<struct example_1>(a).i1 << " ; " << std::any_cast<struct example_1>(a).i2 << '\n';
// bad cast
try
{
a = 1;
std::cout << std::any_cast<float>(a) << '\n';
}
catch (const std::bad_any_cast& e)
{
std::cout << e.what() << '\n';
}
// has value
a = 1;
if (a.has_value())
{
std::cout << a.type().name() << '\n';
}
// reset
a.reset();
if (!a.has_value())
{
std::cout << "no value\n";
}
// pointer to contained data
a = 1;
int* i = std::any_cast<int>(&a);
std::cout << *i << "\n";
}
输出如下:
i: 1
d: 3.14
b: 1
Z4mainE9example_1: 1 ; 2
bad any_cast
i
no value
1
在基本类型的情况下很容易,但在结构的情况下,我们有 return 调用 std::any.type().name()
结构的符号。
我认为要将传递的 std::any
参数转换为正确的类型,我可以在符号字符串中搜索是否包含子字符串 "structname" 或 "classname"。但我认为这只是一种解决方法。有没有办法有效地将 std::any
转换为正确的类型?
另一个问题,如果我在我的库中使用它,我不知道传递的对象的结构名称或 class(因此我的解决方法不起作用)。有没有办法检索它并使用 std::any_cast
正确的类型?
这是因为将在编译期间进行优化。如果你想在 runtime
上获得一个类型的全名,你必须 demangle
类型的名称(这不是标准的)。
假设您的类型名称在字符串变量中 type_name
:
int demangle_status;
char* demangled_name = abi::__cxa_demangle(type_name.c_str(), nullptr, nullptr, &demangle_status;
if (demangle_status == 0)
{
type_name = demangled_name;
std::free(demangled_name)
}
std::cout << type_name
请注意,您必须包含 <cxxabi.h>
才能调用 abi::__cxa_demangle
.type().name()
返回的值完全由实现定义,您无法以任何方式保证它们。您甚至无法保证 .type().name()
对于多个不同类型是不同的。
即使情况并非如此.type().name()
returns 一个运行时值并且您不能使用非常量值来计算类型。
C++ 是静态类型的。每个表达式(在模板实例化之后)都有一个在编译时固定的类型。因此,如果不了解它(可能)持有的类型,您就无法使用 std::any
。
您可以使用 any_cast
或 .type()
返回的 std::type_info
获取 std::any
持有的类型和值(通过它在.name()
),只有当你有一组有限的可能类型时,在编译时确定,它可以保存在该代码位置(在模板实例化之后)。如果您不知道它在您的代码中的给定位置(在模板实例化之后)的潜在类型,那么您将无法获得它在那里的类型或值。
如果您的库的用户打算将他们的类型与您的 std::any
一起使用,而您实际上想提取库代码中的值,那么您将需要模板化您的代码,而用户将需要提供潜在类型作为模板参数。
很少有使用 std::any
的情况。通常 std::variant
或虚拟基础 class 接口(用户可以从中派生出自己的类型)更可能是合适的工具。或者,如果实际上不需要运行时多态性(我们不知道您的用例),只需编写用户可以提供其自定义类型的模板代码就足够了。
另请注意:不要在 class 定义或显式前向声明之外的 C++ 类型名称前使用 struct
。这不是必需的,但如果类型的名称查找失败,可能会产生意想不到的后果。