C++ 避免使用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

我想在我的程序中使用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 (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
?我认为一个有效的答案可能是