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传统遗留下来的遗留兼容性,并且在很多情况下调用未定义(通常是中断的)行为
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::coutAstd::vector
是一种与Astd::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;i std::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 );
}