C++ std::vector的C风格转换

C++ std::vector的C风格转换,c++,c,c++11,casting,static-cast,C++,C,C++11,Casting,Static Cast,在试图找到将std::vector转换为std::vector的解决方案时,我在现有代码库中偶然发现了这个实现。我正在使用C++11 考虑以下代码段: #include <iostream> #include <vector> class A { // some implementation details }; class B : public A { // some implementation details }; void count(std

在试图找到将
std::vector
转换为
std::vector
的解决方案时,我在现有代码库中偶然发现了这个实现。我正在使用C++11

考虑以下代码段:

#include <iostream>
#include <vector>

class A
{
    // some implementation details
};

class B : public A
{
    // some implementation details
};

void count(std::vector<A *> const & a_vec)
{
  std::cout << "IT HAS THESE MANY PTRS: " << a_vec.size() << std::endl;
}

int main()
{
  B * b;

  std::vector<B *> b_vec {b};
  count((std::vector<A *> &) b_vec);

  return 0;
}
它按照预期进行编译和工作。现在我有以下问题:

1) 为什么第一个代码段甚至可以编译,但使用
静态\u cast
会导致编译错误

2) 这两种方法的计算成本是多少?由于创建临时向量对象
new_vec
,我预计第二个向量将产生额外的成本,但我不确定

3) 在这些情况下使用C样式转换有哪些缺点

谢谢

为什么第一个代码段甚至可以编译,但使用静态转换会导致编译错误

因为C型石膏是一把大锤,它会把所有的小心抛到九霄云外。它的座右铭是“你想要它?你得到了它”,不管“它”是什么。静态转换只会执行静态类型检查方面正确的转换

这两种方法的计算成本是多少?我预计由于创建临时向量对象new_vec,第二个将产生额外的成本,但我不确定

你的期望是正确的。但是,具有定义良好语义的代码的成本可能会增加程序的工作量

在这些情况下使用C样式转换有哪些缺点

它将始终编译,并且在将来的某个平台上尝试运行它之前,您不会发现有问题。因为它今天可能有用

<> Lee > C++中有许多情况,其中规范允许编译器不给出错误,但是在哪里得到的程序的行为是未定义的。C样式转换在很大程度上是C传统遗留下来的遗留兼容性,并且在很多情况下调用未定义(通常是中断的)行为
  • 从理论上讲,编译器可以对其进行优化,但很可能是的,它会产生一些计算成本。它可能比调用所有这些对象的开销要小,您可能会在转换它们之后这样做
  • C-style cast的缺点是它不会阻止您调用未定义的行为,也不会明确您的意图(例如,使用
    auto x=(Foo)someConstType
    ,您是想删除
    const
    限定符还是出于意外?)

  • 在您的特定情况下,如果您有多个继承,C风格的版本将生成一个错误的程序,向上投射指针意味着其地址需要更改以指向相应的基类对象。

    这段代码毫无意义。不要求
    派生的*
    基*
    相同,因此告诉编译器假装
    std::vector
    std::vector
    是不需要的。事实上,如果您有多个相同类型的基,那么这种指针类型的双关语是不可能的。试试看:

    #include <iostream>
    
    struct Base {
        int i;
    };
    
    struct I1 : Base {
        int j;
    };
    
    struct I2 : Base {
        int k;
    };
    
    struct Derived : I1, I2 {
        int l;
    };
    
    int main() {
        Derived d;
        Base* b1 = &(I1&)d;
        Base* b2 = &(I2&)d;
        std::cout << (void*)&d << ' ' << (void*)b1 << ' ' << (void*)b2 << '\n';
        return 0;
    }
    
    #包括
    结构基{
    int i;
    };
    结构I1:基础{
    int j;
    };
    结构I2:基础{
    int k;
    };
    派生结构:I1,I2{
    int l;
    };
    int main(){
    导出d;
    基*b1=&(I1&)d;
    基*b2=&(I2&)d;
    std::coutA
    std::vector
    是一种与A
    std::vector
    无关的类型。没有合法的方法将一个的内存解释为另一个,除非是像放置新的这样愚蠢的东西

    如果你幸运的话,你的尝试会产生错误。如果你不幸运的话,它们会产生未定义的行为,这意味着它今天似乎可以正常工作,但明天它们可以默默地格式化你的硬盘,因为任何事情,从编译升级到代码被更改,或是月亮的相位

    现在,在
    向量
    上的许多操作都在
    向量
    上工作。我们可以通过类型擦除来处理这个问题

    下面是一个低效类型擦除类:

    template<class R, class...Args>
    using vcfunc = std::function<R(void const*, Args...)>;
    
    template<class T, class R, class...Args, class F>
    vcfunc<R,Args...> vcimpl( F&& f ) {
      return [f=std::forward<F>(f)](void const* pt, Args&&...args)->R{
        return f( *static_cast<T const*>(pt), std::forward<Args>(args)... );
      };
    }
    template<class T>
    struct random_access_container_view {
      using self=random_access_container_view;
      struct vtable_t {
        vcfunc<std::size_t> size;
        vcfunc<bool> empty;
        vcfunc<T, std::size_t> get;
      };  
      vtable_t vtable;
      void const* ptr = 0;
      template<class C,
        class dC=std::decay_t<C>,
        std::enable_if_t<!std::is_same<dC, self>{}, int> =0
      >
      random_access_container_view( C&& c ):
        vtable{
          vcimpl<dC, std::size_t>( [](auto& c){ return c.size(); } ),
          vcimpl<dC, bool>( [](auto& c){ return c.empty(); } ),
          vcimpl<dC, T, std::size_t>( [](auto& c, std::size_t i){ return c[i]; } )
        },
        ptr( std::addressof(c) )
      {}
    
      std::size_t size() const { return vtable.size( ptr ); }
      bool empty() const { return vtable.empty( ptr ); }
      T operator[](std::size_t i) const { return vtable.get( ptr, i ); }
    };
    
    模板
    使用vcfunc=std::function;
    模板
    vcfunc vimpl(F&&F){
    返回[f=std::forward(f)](无效常量*pt,Args&…Args)->R{
    返回f(*静态转换(pt),标准::正向

    结构A{ char name='A'; }; 结构B:A{ B(){name='B';} }; 无效打印(随机访问容器查看容器){ 对于(std::size_t i=0;istd::cout 1)仅仅因为编译了某些东西并不意味着它会工作,也不意味着它会产生正确的结果。2)用正确的方式做事通常比使用非法的快捷方式更费事。3)缺点是它是未定义的行为。C风格的强制转换告诉编译器“闭嘴做吧!”。这“很好”错误或警告消息,但它是否真正起作用取决于您。编译器不会告诉您,因为您只是要求它不要这样做。运行时多态性和STL不能很好地混合。这是另一种选择,
    template<class R, class...Args>
    using vcfunc = std::function<R(void const*, Args...)>;
    
    template<class T, class R, class...Args, class F>
    vcfunc<R,Args...> vcimpl( F&& f ) {
      return [f=std::forward<F>(f)](void const* pt, Args&&...args)->R{
        return f( *static_cast<T const*>(pt), std::forward<Args>(args)... );
      };
    }
    template<class T>
    struct random_access_container_view {
      using self=random_access_container_view;
      struct vtable_t {
        vcfunc<std::size_t> size;
        vcfunc<bool> empty;
        vcfunc<T, std::size_t> get;
      };  
      vtable_t vtable;
      void const* ptr = 0;
      template<class C,
        class dC=std::decay_t<C>,
        std::enable_if_t<!std::is_same<dC, self>{}, int> =0
      >
      random_access_container_view( C&& c ):
        vtable{
          vcimpl<dC, std::size_t>( [](auto& c){ return c.size(); } ),
          vcimpl<dC, bool>( [](auto& c){ return c.empty(); } ),
          vcimpl<dC, T, std::size_t>( [](auto& c, std::size_t i){ return c[i]; } )
        },
        ptr( std::addressof(c) )
      {}
    
      std::size_t size() const { return vtable.size( ptr ); }
      bool empty() const { return vtable.empty( ptr ); }
      T operator[](std::size_t i) const { return vtable.get( ptr, i ); }
    };
    
    struct A {
        char name='A';
    };
    
    struct B:A {
        B(){ name='B'; }
    };
    
    void print_them( random_access_container_view<A> container ) {
        for (std::size_t i = 0; i < container.size(); ++i ) {
            std::cout << container[i].name << "\n";
        }
    }
    int main() {
        std::vector<B> bs(10);
        print_them( bs );
    }