std::any_cast()和std::get_if(std::variant)是否绝对需要以指针作为参数?

7

对于 std::anystd::variant,我们有函数来请求对象的当前包含值,并返回 nullptr,如果请求无法满足(就像 dynamic_cast 一样):

template<class ValueType>
const ValueType *any_cast(const any *operand);

template<class ValueType>
ValueType *any_cast(any *operand);

并且

template <class T, class... Types>
std::add_pointer_t<T> get_if(variant<Types...> *pv);

template <class T, class... Types>
std::add_pointer_t<const T> get_if(const variant<Types...> *pv);

两者都将指针作为参数。为什么?这不是高效的。实现每次都必须检查参数是否不是nullptrnullptr参数有任何意义吗?

这些函数可以作为类成员或者采用引用作为参数(名称可能略有不同)。为什么会出现这种次优设计的原因?只是模仿dynamic_cast接口吗?


4
你正在使用一个类型擦除的容器并获取其存储的值,而你担心与nullptr进行比较的性能? - Nicol Bolas
1
是的,我担心每一个可以避免的开销。而且这只是一个丑陋的接口。 - Victor Dyachenko
1
有一些函数的版本接受引用作为参数。它们在失败时会抛出异常(而不是返回 nullptr)。 - Barry
“它们是断言而不是请求。”考虑为什么要发出这样的请求。我相当确定,variant访问者比使用一系列get_if调用的任何代码性能都要优越。至于any,像你这样测试几个替代方案的代码无论如何都会有可怕的性能(更不用说阅读时的普遍丑陋)。简而言之,您已经处于次优路径上了;如果它比可能的情况略微不够优化,那又有谁在乎呢? - Nicol Bolas
1
@Nicol 有道理。但这并不是我的问题的答案。 - Victor Dyachenko
显示剩余3条评论
1个回答

6
为了将函数名限制为其中一个,并使其像内置的_cast运算符一样工作,any_cast可以使用引用或指针来工作。
指针版本接受一个指针,如果它包含您要求的内容,则返回该元素的指针。否则它会返回nullptr。
引用版本接受一个引用,并在它不包含您要求的内容时抛出异常。
他们利用了参数的指针性质来区分这两种选项,这匹配了dynamic_cast<T&>(x)dynamic_cast<T*>(&x)的工作方式。
它很容易内联。对于指向自动存储对象的指针进行的内联检查易于优化为“非空”,因为没有符合标准的方法可以使自动存储对象的地址为nullptr。
因此,在发布时,在几乎所有情况下,我都预计从检查是否为nullptr中不会有任何开销。唯一的例外是代码具有指向任何(或变量)的指针,它有一些证据表明这个指向任何的指针不为空,编译器不太可能知道,然后将其传递给any_cast。如果他们没有使用指向指针返回类型的技巧,那么可以通过any_cast_to_ptr(any&)“冒险”UB并无条件地解除指针引用。
真正缺失的API是dangerous_any_cast,如果类型不匹配,则会简单地进行UB,因为关于any状态的非本地证明知识似乎比关于指向任何的空性的非本地证明知识更有可能。
这样的情况很少见。
至于get_ifget之间的区别,我不知道为什么没有get_if(variant<???>&)重载。

3
get_if 只能用于指针。引用/强制版本是 get - T.C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接