Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it absolutely necessary for std::any_cast() and std::get_if(std::variant) to take pointer as an argument?

Tags:

c++

c++17

For std::any and std::variant we have functions to request the object about current contained value, that return nullptr if the request cannot be satisfied (just like dynamic_cast does):

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

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

and

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);

Both take pointer as an argument. Why? It is not efficient. The implementation has every time to check if the argument is not nullptr. Does nullptr argument makes any sense at all?

This functions can be class members or take a reference as an argument (may be with slightly different names). What are reasons for suboptimal design like this? Just mimic dynamic_cast interface?

like image 893
Victor Dyachenko Avatar asked Jul 27 '16 15:07

Victor Dyachenko


People also ask

What is the purpose of std :: any?

std::any. The class any describes a type-safe container for single values of any copy constructible type. 1) An object of class any stores an instance of any type that satisfies the constructor requirements or is empty, and this is referred to as the state of the class any object.

Can std :: variant be empty?

Empty variants are also ill-formed (std::variant<std::monostate> can be used instead). A variant is permitted to hold the same type more than once, and to hold differently cv-qualified versions of the same type.

Does std :: Any allocate memory?

std::any uses Small Buffer Optimization, so it will not dynamically allocate memory for simple types like ints, doubles… but for larger types it will use extra new . std::any might be considered 'heavy', but offers a lot of flexibility and type-safety.

Is std :: Any slow?

Performance of std::any Also, invoking a std::any_cast to retrieve the value is quite slow compared to std::variant . The Boost equivalent of std::any , boost::any , provides a fast version of std::any_cast called boost::any_cast_unsafe which can be utilized if you know which type is contained.


1 Answers

To keep the function name down to one of them, and make it work like the built in _cast operators, any_cast works with either references or pointers.

The pointer versions take a pointer, and if it contains what you ask for, returns a pointer to the element. Otherwise it returns nullptr.

The reference versions take a reference, and throws if it does not contain what you ask for.

They used the pointer-ness of the argument to distinguish between those two options, matching how dynamic_cast<T&>(x) and dynamic_cast<T*>(&x) work.

It is going to be easy to inline. Inline checks against null of a pointer-to-automatic-storage object are easy to optimize to be "not null", as there are no conformant ways for an automatic storage object's address to be nullptr.

So in almost every case in release I would expect zero overhead from the check-if-nullptr. The one exception is where the code has a pointer-to-any (or variant), it has some proof that this pointer-to-any is not null that the compiler is unlikely to know, and it then passes that to any_cast. If they had not used the pointer-to-indicate-pointer-return-type trick, one could "risk" UB with the any_cast_to_ptr(any&) and unconditionally dereferencing the pointer.

The real API missing is the dangerous_any_cast, which simply does UB if the type does not match, because nonlocally proven knowledge about the state of the any seems more likely than nonlocally proven knowledge about the nullity of a pointer-to-any.

Such cases are rare.

As for get_if vs get, I do not know why there is no get_if(variant<???>&) overload.

like image 185
Yakk - Adam Nevraumont Avatar answered Oct 01 '22 02:10

Yakk - Adam Nevraumont