blackstar

V1

2022/07/15阅读:13主题:默认主题

any类型转换之谜

背景

struct A{

  A(int a){
    this->a = a;
  }
  
  int a;
};

namespace Test{

  struct A{
    
    A(int a){
      this->a = a;
    }
    
    int a;
  };
}

void AnyCast(std::any& param){

    const auto value = std::any_cast<Test::A>(param).a;
    std::cerr << "value : " << value << std::endl;
}

int main(){
    
    A a = 1;
    auto param = std::make_any<A>(a);
    AnyCast(param);
    
    return 0;
}

  • 上面转换是否成功?

疑问1

为什么自定义内存结构完全相同的同一类型名,仅仅因为命名空间不同就会导致转换异常?去除命名空间是否正常? 为了保证编译通过,需要将自定义类型在不同模块分开。

// module.h
#pragma once
#include <memory>
#include <any>

class ModuleImpl;

class Module{
public:
    Module();

    void AnyCast(std::any& param);
private:
    std::shared_ptr<ModuleImpl> _impl;
};

// module.cpp

#include "module.h"
#include <iostream>

struct A {

    A(int a) {
        this->a = a;
    }

    int a;
};

class ModuleImpl {
    
public:
    void AynyCast(std::any& param)
    
{
        const auto value = std::any_cast<A>(param).a;
        std::cerr << "value : " << value << "\n";
    }
};


Module::Module()
    : _impl(std::make_shared<ModuleImpl>())
{
}

void Module::AnyCast(std::any& param)
{
    _impl->AynyCast(param);
}

  • 上面转换还会出现异常吗?

疑问2

是不是分开模块,加命名空间也不会有问题?针对这个疑问,不是不可能,验证下:

有种时曾相识的感觉。

揭开any_cast 的面纱

这里以MSVC 版本实现的 any_cast 为例。首先我们看下它的实现逻辑:

template <class _Ty>
_NODISCARD remove_cv_t<_Ty> any_cast(any& _Any) {

    static_assert(is_constructible_v<remove_cv_t<_Ty>, _Remove_cvref_t<_Ty>&>,
        "any_cast<T>(any&) requires remove_cv_t<T> to be constructible from remove_cv_t<remove_reference_t<T>>&");

    const auto _Ptr = _STD any_cast<_Remove_cvref_t<_Ty>>(&_Any);
    if (!_Ptr) {
        _Throw_bad_any_cast();
    }

    return static_cast<remove_cv_t<_Ty>>(*_Ptr);
}

关键函数:any_cast<_Remove_cvref_t<_Ty>>(&_Any)

template <class _ValueType>
_NODISCARD _ValueTypeany_cast(anyconst _Anynoexcept {

    // retrieve a pointer to the _ValueType contained in _Any, or null
    static_assert(!is_void_v<_ValueType>, "std::any cannot contain void.");

    if constexpr (is_function_v<_ValueType> || is_array_v<_ValueType>) {
        return nullptr;
    } else {
        if (!_Any) {
            return nullptr;
        }

        return _Any->_Cast<_Remove_cvref_t<_ValueType>>();
    }
}

关键函数:_Any->_Cast<_Remove_cvref_t<_ValueType>>()

 template <class _Decayed>
    _NODISCARD _Decayed* _Cast() noexcept {
 // if *this contains a value of type _Decayed, return a pointer to it
        return const_cast<_Decayed*>(static_cast<const any*>(this)->_Cast<_Decayed>());
    }

关键函数:static_cast<const any>(this)->_Cast<_Decayed>()*

 template <class _Decayed>
    _NODISCARD const _Decayed* _Cast() const noexcept {

        // if *this contains a value of type _Decayed, return a pointer to it
        const type_info* const _Info = _TypeInfo();
        if (!_Info || *_Info != typeid(_Decayed)) {
            return nullptr;
        }

        if constexpr (_Any_is_trivial<_Decayed>) {
            // get a pointer to the contained _Trivial value of type _Decayed
            return reinterpret_cast<const _Decayed*>(&_Storage._TrivialData);
        } else if constexpr (_Any_is_small<_Decayed>) {
            // get a pointer to the contained _Small value of type _Decayed
            return reinterpret_cast<const _Decayed*>(&_Storage._SmallStorage._Data);
        } else {
            // get a pointer to the contained _Big value of type _Decayed
            return static_cast<const _Decayed*>(_Storage._BigStorage._Ptr);
        }
    }

本次问题的关键代码:

// if *this contains a value of type _Decayed, return a pointer to it
        const type_info* const _Info = _TypeInfo();
        if (!_Info || *_Info != typeid(_Decayed)) {
            return nullptr;
        }

可以用一行代码说明:

typeid(A) == typeid(Test::A)

成立吗?

废话不多说,直接开干:

struct A {

    A(int a) {
        this->a = a;
    }

    int a;
};

namespace Test {

    struct A {

        A(int a) {
            this->a = a;
        }

        int a;
    };
}

int main()
{
    const auto same = typeid(A) == typeid(Test::A);

    std::cerr << "same : " << std::boolalpha << same << std::endl;

    return 0;
}

总结

作为一名合(摸)格(鱼)的软件开发工程师,最后总结一下不过分吧。

  • 在使用any类型转换的时候,一定要保证类型来源唯一
  • 不同模块之间不要随便拷贝代码
  • 只有代码才能解决代码中的BUG

本次 any类型转换之谜完结撒花!!!

分类:

后端

标签:

C++

作者介绍

blackstar
V1

一个常年混迹于摸鱼网站的码农