我想在程序中使用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);
}
请注意,为简洁起见,我省略了其他很多内容。
我的程序可以使用任何可以存储在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”错误。我该如何重构它以避免在整个程序中多次写入较大的if / else块?
答案 0 :(得分:3)
如果您有已知的可能类型的固定列表,请不要使用std::any
。使用std::variant<Ts...>
。这使得Dietmar's的答案看起来像这样:
#include <variant>
void test(std::variant<int, double, char const*> v)
{
std::visit([](auto value){ std::cout << "value=" << value << "\n"; }, v);
}
这是同一件事,除了(a)您不必自己实现visit
(b)运行时效率更高,并且(c)类型安全-您不会忘记检查特定类型!真的,即使您不在乎(a)或(b),(c)也是一个巨大的胜利。
如果您没有有一个已知的,可能的类型的固定列表-这是想要std::any
的典型用例-那么您在{{ 1}}毫无意义。您无法枚举所有可能的可复制类型(有无限数量的可复制类型),因此不必检索内容。所以我真的认为std::any
是您想要的。
答案 1 :(得分:1)
好吧,如果您确定需要存储在 ...... p
angular
.module('myApp', [])
.controller('myController', ($scope, $timeout, $interval) => {
var interval;
var timeout;
$scope.times = 0;
$scope.mouseUp = function(){
$timeout.cancel(timeout);
$interval.cancel(interval);
};
let increment = ()=>{
$scope.times++;
};
$scope.mouseDown = function(){
increment();
timeout = $timeout(()=>{
interval = $interval(()=>{
increment();
},100)
}, 1000)
};
});
%-}
答案 2 :(得分:1)
基本思想是创建一个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"; });
}
下一步是删除一个类型限制。由于显式模板参数排在第一位,并且是一个不限成员名额的列表,而函数对象应该是推导的模板参数,因此您不能完全使用函数模板。但是,可变模板(当然带有inline constexpr
,因此是 variable ...)可以达到目的:
#include <any>
#include <iostream>
#include <type_traits>
template <typename... T>
inline constexpr auto any_visit =
[](auto&& any, auto visit) -> std::enable_if_t<std::is_same_v<std::any, std::decay_t<decltype(any)>>> {
(
(any.type() == typeid(T) && (visit(std::any_cast<T>(std::forward<decltype(any)>(any))), true))
|| ...)
// Uncomment the line below to have visit(any) called for unhandled types
// || (visit(std::forward<decltype(any)>(any)), true)
;
};
void test(std::any any)
{
any_visit<int, double, char const*>(any, [](auto value){ std::cout << "value=" << value << "\n"; });
}
int main() {
test(17);
test(3.14);
test(+"foo");
}
如果您需要解码多个std::any
个对象,您只需向其中传递适当的[lambda?]函数即可,该函数引用其他对象并不断构建该对象,直到获得所需的所有对象为止。
答案 3 :(得分:1)
我觉得编写这种代码很有趣。
(defclass myclass ()
((s1
:initform '((a . 1) (b . 2)))
(s2
:initform '((a . 1) (b . 3)))))
是访问一组类型的函数对象。
您可以先调用any,然后再调用功能对象。然后,它将使用any_visitor<types...>
中的types...
中的任何一个调用函数对象。
您这样做了any
。
如果any_vistor<int, double>{}( something, [](auto&& x) { /* some code */ } )
中都没有types...
,它将用any
调用函数对象,以处理多余的情况。
我们还可以编写一个变体,而不是将std::any
传递给函子,而是抛出或返回false或其他内容。
std::any
我还添加了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>...>;
,这使发送变得更容易。如果要统一处理所有类型,除了处理错误情况,可以执行以下操作:
overloaded
并将其作为函数对象传递给overloaded{
[](auto const& x){ std::cout << x << "\n"; },
[](std::any const&){ std::cout << "Unknown type\n"; }
}
。
以下是一些测试代码:
any_visitor
输出:
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)来映射存储在我们调用的函数对象的any中存储的类型的索引。
另一种方法是编写:
7
Unknown
,如果类型匹配,它将template<class...Ts>
std::optional<std::variant<Ts...>> to_variant( std::any );
转换为变量。然后在std::any
上使用通常的访问机制,而不用自己动手。