繁体   English   中英

std :: vector的C样式转换

[英]C-style cast of std::vector

当试图找到将std::vector<Derived *>转换为std::vector<Base *>的解决方案时,我在现有的代码库中偶然发现了此实现。 我正在使用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;
}

感觉很狡猾,所以我试图找到一个替代方案。 这篇文章提出了一种使用std::vector::assign 所以现在,我的主要功能如下所示:

int main()
{
  B * b;

  std::vector<B *> b_vec {b};
  std::vector<A *> new_vec;
  new_vec.assign(b_vec.begin(), b_vec.end());
  count(new_vec);

  return 0;
}

它可以按预期进行编译和工作。 现在我有以下问题:

1)为什么第一个代码片段甚至可以编译,但是使用static_cast会导致编译错误?

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

3)在这种情况下,使用C样式转换有哪些弊端?

谢谢。

为什么第一个代码段甚至可以编译,但是使用static_cast会导致编译错误?

因为C型投掷是一把大锤,将使一切警惕。 它的座右铭是“您想要它吗?您得到了它”,无论它是什么。 静态类型转换只能执行在静态类型检查方面正确的类型转换。

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

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

在这些情况下,使用C样式转换有哪些弊端?

它会一直编译,并且直到将来尝试在某个平台上运行它时,您才发现存在问题。 因为它今天可能会工作。

该代码是胡说八道。 没有要求,即a的 Derived*是相同的a的 Base* ,因此告诉编译器假装一个std::vector<B*>是一个std::vector<A*>是不要求对任何明智的事情。 实际上,如果您有多个相同类型的碱基,则该指针类型pun是不可能的。 试试吧:

#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;
}
  1. 在C ++中,很多情况下规范都允许编译器不给出错误,但结果程序的行为未定义。 C样式强制转换很大程度上是C遗产遗留下来的遗留兼容性,并且在很多情况下会调用未定义(通常是损坏的)行为。
  2. 从理论上讲,编译器可以对其进行优化,但是很可能是这样,它将产生一些计算成本。 它可能比调用所有这些对象的开销要小,这大概是在投射它们之后要做的。
  3. C样式转换的缺点是,它不会阻止您调用未定义的行为,也无法清楚说明您的意图(例如,使用auto x = (Foo) someConstType ,您是要删除const限定词还是那是偶然吗?)。

在您的特定情况下,如果您具有多重继承,则C样式的版本将产生不正确的程序,并且向上转换指针意味着需要更改其地址以指向适当的基类对象。

std::vector<Derived*>是与std::vector<Base*>不相关的类型。 没有合法的方法可以将一个人的记忆解释为另一个人的记忆,只是缺少像新安置这样的疯狂愚蠢的东西。

如果幸运的话,您的尝试会产生错误。 如果不是这样,它们会产生不确定的行为,这意味着它似乎在今天可以正常工作,但是明天,由于从编译器升级,更远的代码更改或月球阶段的各种变化,它们可以以无提示的方式格式化硬盘。

现在,情况是在vector<Base*>上进行的许多操作都在vector<Derived*> 我们可以用类型擦除来解决这个问题。

这是一个低效率的类型擦除类:

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

允许将子容器视为基本列表的语言基本上会自动执行上述操作。 容器本身具有与虚拟功能表等效的功能,或者当您将容器视为基于虚拟功能表的视图时,则由客户端代码合成和使用。

上面的代码效率不是最高; 请注意,每个std::function都是无状态的。 我可以很轻松地用函数指针替换它们,并基于C类型存储一个静态vtable来节省内存(但添加另一个间接寻址)。

我们也可以使用非视图类型来简化此操作,因为我们可以使用类型擦除模型概念模式代替此手动vtable模式。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM