簡體   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