std::any
を使用する場合は、RTTIをオフにして使用できます。次の例は、gccを使用した-fno-rtti
でも期待どおりにコンパイルおよび実行されます。
int main()
{
std::any x;
x=9.9;
std::cout << std::any_cast<double>(x) << std::endl;
}
しかし、どのようにstd::any
が型情報を保存するのでしょうか?ご覧のとおり、「間違った」タイプでstd::any_cast
を呼び出すと、予想どおりstd::bad_any_cast
例外が発生しました。
それはどのように実現されますか、これはおそらくgcc機能のみですか?
boost::any
もRTTIを必要としないことがわかりましたが、それを解決する方法も見つかりませんでした。 boost :: anyはRTTIを必要としますか? 。
STLヘッダー自体を掘り下げても、答えは得られません。そのコードは私にはほとんど読めません。
TL; DR;std::any
は、テンプレートクラスの静的メンバー関数へのポインタを保持します。この関数は多くの操作を実行でき、関数の実際のインスタンスはクラスのテンプレート引数に依存するため、特定の型に固有です。
Libstdc ++でのstd::any
の実装はそれほど複雑ではないので、ご覧ください。
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/any
基本的に、std::any
は2つのことを保持します。
void (*_M_manager)(_Op, const any*, _Arg*);
タイプT
のオブジェクトで新しいstd::any
を作成または割り当てると、_M_manager
はタイプT
に固有の関数を指します(実際には静的メンバーです) T
に固有のクラスの関数):
template <typename _ValueType,
typename _Tp = _Decay<_ValueType>,
typename _Mgr = _Manager<_Tp>, // <-- Class specific to T.
__any_constructible_t<_Tp, _ValueType&&> = true,
enable_if_t<!__is_in_place_type<_Tp>::value, bool> = true>
any(_ValueType&& __value)
: _M_manager(&_Mgr::_S_manage) { /* ... */ }
この関数は特定の型に固有であるため、std::any
で必要な操作を実行するためにRTTIは必要ありません。
さらに、std::any_cast
内で正しい型にキャストしていることを簡単に確認できます。 std::any_cast
のgcc実装のコアは次のとおりです。
template<typename _Tp>
void* __any_caster(const any* __any) {
if constexpr (is_copy_constructible_v<decay_t<_Tp>>) {
if (__any->_M_manager == &any::_Manager<decay_t<_Tp>>::_S_manage) {
any::_Arg __arg;
__any->_M_manager(any::_Op_access, __any, &__arg);
return __arg._M_obj;
}
}
return nullptr;
}
これは、キャストしようとしているオブジェクト内のストアド関数(_any->_M_manager
)とキャストしたいタイプのマネージャー関数(&any::_Manager<decay_t<_Tp>>::_S_manage
)間の単純なチェックであることがわかります。
クラス_Manager<_Tp>
は、実際には_Manager_internal<_Tp>
に応じて_Manager_external<_Tp>
または_Tp
のエイリアスです。このクラスは、std::any
クラスのオブジェクトの割り当て/構築にも使用されます。
考えられる解決策の1つは、any
に格納されている可能性のあるすべてのタイプに対して一意のIDを生成することです(any
が内部的にどのように機能するかは知らないと思います)。それを行うことができるコードは次のようになります。
struct id_gen{
static int &i(){
static int i = 0;
return i;
}
template<class T>
struct gen{
static int id() {
static int id = i()++;
return id;
}
};
};
これを実装すると、RTTI typeinfo
の代わりにタイプのIDを使用して、タイプをすばやく確認できます。
関数および静的関数内での静的変数の使用に注意してください。これは、静的変数の初期化の未定義の順序の問題を回避するために行われます。
限られたRTTIの手動実装はそれほど難しくありません。静的汎用関数が必要になります。完全な実装を提供しなくても、それだけ言えます。ここに1つの可能性があります。
class meta{
static auto id(){
static std::atomic<std::size_t> nextid{};
return ++nextid;//globally unique
};
std::size_t mid=0;//per instance type id
public:
template<typename T>
meta(T&&){
static const std::size_t tid{id()};//classwide unique
mid=tid;
};
meta(meta const&)=default;
meta(meta&&)=default;
meta():mid{}{};
template<typename T>
auto is_a(T&& obj){return mid==meta{obj}.mid;};
};
これは私の最初の観察です。理想とはほど遠い、多くの詳細が欠けています。 meta
の1つのインスタンスを、std::any
の想定される実装の非静的データメンバーとして使用できます。