Объединение static_cast и std::any_cast

#c #c 17

#c #c 17

Вопрос:

Существует ли какая-либо безопасная std::any_cast static_cast комбинация и?

Я пытаюсь выполнить следующее:

 #include <any>
#include <iostream>

int main( )
{
    auto x = std::make_any< int >( 5 );
#if 0 // doesn't work
    std::cout << std::any_cast< short >( x );
#else // works, but requires knowing the initial type
    std::cout << static_cast< short >( std::any_cast< int >( x ) );
#endif
}
 

Комментарии:

1. Тип x is std::any . Это std::any_cast<stored_type> предпочтительный и самый безопасный способ, что бы это ни значило при использовании std::any .

Ответ №1:

Единственный способ получить значение из std::any any_cast<T> where T имеет то typeid же значение, что и значение внутри (вы можете проверить его с .type() помощью метода).

Если вам нужна другая семантика, например, «принять значение, если оно преобразуется в int «, вы должны использовать что-то другое для стирания типа. Например, вы можете написать его самостоятельно.

Комментарии:

1. Как можно было бы написать свои собственные std::any с такой функциональностью? Как отслеживать static_cast конверсии? Или они должны быть жестко запрограммированы?

2. @j5w Я подозреваю , что выступление Артура О’Дуайера на CppCon 2019 «Назад к основам» о стирании типов дает хороший обзор, хотя я сам его не смотрел

3. Отличное видео! В нем даже рассматривается подход к предоставлению dynamic_cast возможностей. К сожалению, он ничего не решает static_cast , поэтому мне может потребоваться жестко запрограммировать его и отслеживать typeid вместо того, чтобы полагаться на vtables или RTTI модели исключений.

4. @j5w Я рассматриваю static_cast просто еще одну возможность. Например. virtual optional<int> try_static_cast_to_int() и в реализации есть две ветви: одна для конвертируемой, одна для всех остальных. Вы можете различать два с if constexpr помощью и std::is_convertible во внутреннем шаблонном классе.

Ответ №2:

Дело в том, что std::any это не может иметь смысла в данных, которые хранятся.

Вы можете подумать, что существует функция-член type() , поэтому std::any можете знать тип. Но на самом деле это не помогает, потому что единственное, что вы можете сделать с результатом type() , это сравнить его с каким-либо другим typeid . То есть вы уже должны знать, для какого типа вы проверяете. Следовательно, std::any не может действительно ответить на вопрос «какой тип вы носите?»; он может ответить только на вопрос «вы носите этот тип X?».

Последнее происходит при вызове std::any_cast<short> : std::any только знает, что он не несет a short , но на самом деле не знает, что он несет тип, который можно преобразовать short .

Комментарии:

1. Можно ли в любом случае создать такое автоматическое приведение в качестве расширения std::any , поскольку такое приведение выполняется во время компиляции?

2. @j5w Нет, вы не можете, потому что вы должны расширяться в двух точках: а) в any_cast ; б) во всех вызовах конструктора std::any , которые теоретически могут быть вне вашего контроля.