C++ 避免使用std::any编写相同的重复类型检查代码
我想在我的程序中使用std::any,但我发现自己编写了很多这样的条件语句:C++ 避免使用std::any编写相同的重复类型检查代码,c++,c++14,c++17,C++,C++14,C++17,我想在我的程序中使用std::any,但我发现自己编写了很多这样的条件语句: if (anything.type() == typeid(short)) { auto s = std::any_cast<short>(anything); } else if (anything.type() == typeid(int)) { auto i = std::any_cast<int>(anything); } else if (an
if (anything.type() == typeid(short)) {
auto s = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
auto i = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
auto l = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
auto d = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
auto b = std::any_cast<bool>(anything);
}
#include <variant>
void test(std::variant<int, double, char const*> v)
{
std::visit([](auto value){ std::cout << "value=" << value << "\n"; }, v);
}
if(any.type()==类型ID(简称)){
自动s=std::任意施法(任意);
}else if(anything.type()==typeid(int)){
自动i=std::任意类型(任意);
}else if(anything.type()==typeid(long)){
自动l=std::任意类型(任意);
}else if(anything.type()==typeid(double)){
自动d=std::任意类型(任意类型);
}else if(anything.type()==typeid(bool)){
自动b=std::任意类型(任意);
}
注意,为了简洁起见,我省略了很多其他if条件
我的程序可以使用可以存储在std::any中的任何定义类型,因此这些if-then语句相当长。有没有办法重构代码,这样我就可以编写一次
我最初倾向于使用这样的模板:
template<typename T>
T AnyCastFunction(std::any) {
T type;
if (anything.type() == typeid(short)) {
type = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
type = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
type = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
type = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
type = std::any_cast<bool>(anything);
}
return type;
}
模板
T anycast函数(标准::any){
T型;
if(anything.type()==typeid(short)){
类型=标准::任意类型(任意);
}else if(anything.type()==typeid(int)){
类型=标准::任意类型(任意);
}else if(anything.type()==typeid(long)){
类型=标准::任意类型(任意);
}else if(anything.type()==typeid(double)){
类型=标准::任意类型(任意);
}else if(anything.type()==typeid(bool)){
类型=标准::任意类型(任意);
}
返回类型;
}
但是,这会导致“无法推断模板参数t”错误。我如何重构它以避免在整个程序中多次写入较大的if/else块?好吧,如果您确定需要在
any
中存储如此广泛的范围
template<typename T> void visit(T &&t) { std::cout << "Hi " << t << "!\n"; }
void try_visit(std::any &&) { std::cout << "Unknown type\n"; }
template<typename T, typename... Ts> void try_visit(std::any thing) {
if(thing.type() == typeid(T)) {
visit(std::any_cast<T>(thing));
return;
}
if constexpr(sizeof...(Ts) > 0) try_visit<Ts...>(std::move(thing));
else try_visit(std::move(thing));
}
int main() {
try_visit<short, int, double, bool, long>(std::any{42});
}
template void visit(T&&T){std::cout好吧,如果你确定你需要在any
中存储如此广泛的范围
template<typename T> void visit(T &&t) { std::cout << "Hi " << t << "!\n"; }
void try_visit(std::any &&) { std::cout << "Unknown type\n"; }
template<typename T, typename... Ts> void try_visit(std::any thing) {
if(thing.type() == typeid(T)) {
visit(std::any_cast<T>(thing));
return;
}
if constexpr(sizeof...(Ts) > 0) try_visit<Ts...>(std::move(thing));
else try_visit(std::move(thing));
}
int main() {
try_visit<short, int, double, bool, long>(std::any{42});
}
template void visit(T&&T){std::cout基本思想是创建一个std::any
访问者,并在访问者调用的函数中进行必要的处理。这一基本原则是直截了当的。让我们从只支持一种类型开始:
#include <any>
#include <iostream>
#include <type_traits>
template <typename T, typename Any, typename Visitor>
auto any_visit1(Any&& any, Visitor visit)
-> std::enable_if_t<std::is_same_v<std::any, std::decay_t<Any>>>
{
if (any.type() == typeid(T)) {
visit(std::any_cast<T>(std::forward<Any>(any)));
}
}
int main() {
std::any a0(17);
any_visit1<int>(a0, [](auto value){ std::cout << "value=" << value << "\n"; });
}
如果需要多个std::任何解码的对象,只需传递合适的[lambda?]引用其他对象并不断构建对象的函数。基本思想是创建一个std::any
访问者,并在从访问者调用的函数中执行必要的处理。这一基本原则是直截了当的。让我们从只支持一种类型开始:
#include <any>
#include <iostream>
#include <type_traits>
template <typename T, typename Any, typename Visitor>
auto any_visit1(Any&& any, Visitor visit)
-> std::enable_if_t<std::is_same_v<std::any, std::decay_t<Any>>>
{
if (any.type() == typeid(T)) {
visit(std::any_cast<T>(std::forward<Any>(any)));
}
}
int main() {
std::any a0(17);
any_visit1<int>(a0, [](auto value){ std::cout << "value=" << value << "\n"; });
}
如果需要多个std::任何解码的对象,只需传递合适的[lambda?]引用其他对象并不断构建对象的函数,直到您获得所需的所有对象。如果您有一个已知的、固定的可能类型列表,请不要使用std::any
。使用std::variant
。这使答案看起来像这样:
if (anything.type() == typeid(short)) {
auto s = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
auto i = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
auto l = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
auto d = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
auto b = std::any_cast<bool>(anything);
}
#include <variant>
void test(std::variant<int, double, char const*> v)
{
std::visit([](auto value){ std::cout << "value=" << value << "\n"; }, v);
}
#包括
无效试验(标准:变型v)
{
std::visit([](自动值){std::cout如果您有一个已知的、固定的可能类型列表,不要使用std::any
。使用std::variant
。这会使答案看起来像这样:
if (anything.type() == typeid(short)) {
auto s = std::any_cast<short>(anything);
} else if (anything.type() == typeid(int)) {
auto i = std::any_cast<int>(anything);
} else if (anything.type() == typeid(long)) {
auto l = std::any_cast<long>(anything);
} else if (anything.type() == typeid(double)) {
auto d = std::any_cast<double>(anything);
} else if (anything.type() == typeid(bool)) {
auto b = std::any_cast<bool>(anything);
}
#include <variant>
void test(std::variant<int, double, char const*> v)
{
std::visit([](auto value){ std::cout << "value=" << value << "\n"; }, v);
}
#包括
无效试验(标准:变型v)
{
std::visit([](自动值){std::cout我觉得编写这种代码很有趣
any_visitor
是访问一组类型的函数对象
您使用后跟函数对象的any调用函数对象。然后,它使用类型中的任何一种调用函数对象…
位于any
中
所以你做any_vistor{}(something,[](auto&&x){/*some code*/})
如果类型…
都不在any
中,它将使用std::any
调用函数对象,以便您处理额外的情况
我们还可以编写一个变量,它不是将std::any
传递给functor,而是抛出或返回false或其他内容
template<class...Ts>
struct any_visitor;
template<>
struct any_visitor<> {
template<class F>
decltype(auto) operator()( std::any& a, F&& f ) const {
return std::forward<F>(f)(a);
}
};
template<class...Ts>
struct any_visitor {
private:
struct accum {
std::size_t x = 0;
friend accum operator+( accum lhs, accum rhs ) {
if (lhs.x || rhs.x) return {lhs.x+1};
else return {};
}
};
public:
template<class Any, class F>
void operator()(Any&& any, F&& f) const {
// sizeof...(Ts) none in the list
// otherwise, index of which any is in the list
std::size_t which = sizeof...(Ts) - (accum{} + ... + accum{ any.type() == typeid(Ts) }).x;
using table_entry = void(*)(Any&&, F&&);
static const table_entry table[] = {
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::any_cast<Ts>( std::forward<Any>(any) ) );
}...,
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::forward<Any>(any) );
}
};
table[which]( std::forward<Any>(any), std::forward<F>(f) );
}
};
template<class...Fs>
struct overloaded:Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs&&...)->overloaded<std::decay_t<Fs>...>;
并将其作为函数对象传递给任何访问者
下面是一些测试代码:
std::any foo=7;
std::any bar=3.14;
auto visitor = overloaded{
[](int x){std::cout << x << "\n";},
[](auto&&){std::cout << "Unknown\n";}
};
any_visitor<int>{}( foo, visitor );
any_visitor<int>{}( bar, visitor );
在实现方面,这段代码使用分派表(有点像vtable)来映射存储在函数对象的任何重载中的类型的索引
另一种方法是写:
template<class...Ts>
std::optional<std::variant<Ts...>> to_variant( std::any );
模板
std::可选至_变量(std::任意);
如果类型匹配,它会将std::any
转换为variant。然后使用std::variant
上常用的访问机制,而不是自己滚动。我觉得编写这种类型的代码很有趣
any_visitor
是访问一组类型的函数对象
您使用后跟函数对象的any调用函数对象。然后,它使用类型中的任何一种调用函数对象…
位于any
中
所以你做any_vistor{}(something,[](auto&&x){/*some code*/})
如果类型…
都不在any
中,它将使用std::any
调用函数对象,以便您处理额外的情况
我们还可以编写一个变量,它不是将std::any
传递给functor,而是抛出或返回false或其他内容
template<class...Ts>
struct any_visitor;
template<>
struct any_visitor<> {
template<class F>
decltype(auto) operator()( std::any& a, F&& f ) const {
return std::forward<F>(f)(a);
}
};
template<class...Ts>
struct any_visitor {
private:
struct accum {
std::size_t x = 0;
friend accum operator+( accum lhs, accum rhs ) {
if (lhs.x || rhs.x) return {lhs.x+1};
else return {};
}
};
public:
template<class Any, class F>
void operator()(Any&& any, F&& f) const {
// sizeof...(Ts) none in the list
// otherwise, index of which any is in the list
std::size_t which = sizeof...(Ts) - (accum{} + ... + accum{ any.type() == typeid(Ts) }).x;
using table_entry = void(*)(Any&&, F&&);
static const table_entry table[] = {
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::any_cast<Ts>( std::forward<Any>(any) ) );
}...,
+[](Any&& any, F&& f) {
std::forward<F>(f)( std::forward<Any>(any) );
}
};
table[which]( std::forward<Any>(any), std::forward<F>(f) );
}
};
template<class...Fs>
struct overloaded:Fs... {
using Fs::operator()...;
};
template<class...Fs>
overloaded(Fs&&...)->overloaded<std::decay_t<Fs>...>;
并将其作为函数对象传递给任何访问者
下面是一些测试代码:
std::any foo=7;
std::any bar=3.14;
auto visitor = overloaded{
[](int x){std::cout << x << "\n";},
[](auto&&){std::cout << "Unknown\n";}
};
any_visitor<int>{}( foo, visitor );
any_visitor<int>{}( bar, visitor );
在实现方面,这段代码使用分派表(有点像vtable)来映射存储在函数对象的任何重载中的类型的索引
另一种方法是写:
template<class...Ts>
std::optional<std::variant<Ts...>> to_variant( std::any );
模板
std::可选至_变量(std::任意);
如果类型匹配,它会将std::any
转换为变体。然后使用std::variant
上常用的访问机制,而不是自己滚动。你确定std::any
是你想要的并且不喜欢std::variant
?我认为一个有效的答案可能是