การใช้งาน std::vector ที่มีอยู่ในตัวเองและเข้ากันได้กับ STL

การใช้งาน std::vector ที่มาพร้อมกับ Visual Studio 2010 และเวอร์ชันก่อนหน้านี้มีลักษณะเฉพาะที่รู้จักกันดี: วิธีการ resize มีลายเซ็นต่อไปนี้ (สอดคล้องกับ C ++ 03):

void resize(size_type new_size, value_type value);

แทนที่จะเป็นลายเซ็นที่สอดคล้องกับ C ++ 11 ที่ใช้โดยการใช้งาน STL อื่น ๆ ส่วนใหญ่ (เช่น STL หรือ STLport ของ gcc) นานก่อน C ++ 11:

void resize(size_type new_size, const value_type& value);

ปัญหาของตัวแปรแรกคือ ในบางสถานการณ์ คอมไพล์จะล้มเหลวหาก value_type มีข้อกำหนดการจัดตำแหน่ง:

struct __declspec(align(64)) S { ... };
std::vector<S> v;  // error C2719: '_Val': formal parameter with __declspec(align('64')) won't be aligned

นี่คือดี ปัญหาที่ทราบ และไม่มีวิธีแก้ปัญหาที่น่าพอใจ นอกเหนือจากการใช้การใช้งาน std::vector ที่แตกต่างกัน .

ฉันกำลังมองหาการใช้งาน std::vector ที่เป็นลายลักษณ์อักษรอย่างดี ผ่านการทดสอบอย่างดี มีในตัวเอง และเข้ากันได้กับ STL พร้อมด้วยใบอนุญาตแบบ MIT ที่ฉันสามารถใส่ลงในโปรเจ็กต์ของฉันในฐานะคอนเทนเนอร์ที่เลือกได้ ประเภทที่สอดคล้อง

ฉันคิดว่าจะแยกมันออกจาก STLport หรือ STL ของ gcc แต่เนื่องจากเป็นไปตามมาตรฐานอย่างสมบูรณ์ ทั้งคู่จึงมีขนาดใหญ่และมีการพึ่งพาที่ไม่สำคัญมากมาย

(ฉันจะพอใจอย่างยิ่งกับการใช้งานชุดย่อยที่สมเหตุสมผลของ std::vector ซึ่งจะรองรับเฉพาะ push_back, clear, capacity, size, reserve, resize, swap และการจัดทำดัชนีอาร์เรย์)

มีความคิดอะไรบ้าง?


person François Beaune    schedule 23.02.2012    source แหล่งที่มา
comment
ฉันไม่เห็นว่าเมธอด .resize() ทำให้การประกาศ std::vector<S> v; ล้มเหลวได้อย่างไร การสร้างอินสแตนซ์ของเทมเพลตคลาสไม่ได้สร้างอินสแตนซ์ของวิธีการ แต่เป็นเพียงวิธีที่ใช้เท่านั้น (เช่นค่าเริ่มต้น ctor และ dtor ในกรณีนี้)   -  person MSalters    schedule 23.02.2012
comment
ฉันเดาว่าข้อผิดพลาดเกิดขึ้นระหว่างการแยกวิเคราะห์... โปรดทราบว่าเรากำลังพูดถึงปัญหาเฉพาะของคอมไพเลอร์อย่างแท้จริงที่นี่   -  person François Beaune    schedule 23.02.2012


คำตอบ (2)


ผู้ที่อยู่เบื้องหลังไลบรารี Eigen ดูเหมือนจะพบวิธีแก้ปัญหาที่ดีสำหรับปัญหาการจัดเก็บ "ประเภทที่จัดชิดเกินไป" (ดังที่ Stephan T. Lavavej เรียกพวกเขา) ลงใน std::vector ตามที่นำมาใช้ใน STL ของ Visual Studio

การใช้งานดูเหมือนซับซ้อนโดยไม่จำเป็น (ตรวจสอบแหล่งที่มาที่นี่ และ ที่นี่) แต่แนวคิดหลักคือการสรุปประเภทที่เข้าสู่ std::vector ด้วย กระดาษห่อบาง:

#include <vector>

template <typename T>
struct wrapper : public T
{
    wrapper() {}
    wrapper(const T& rhs) : T(rhs) {}
};

struct __declspec(align(64)) S
{
    float x, y, z, w;
};

int main()
{
    std::vector< wrapper<S> > v;  // OK, no C2719 error
    return 0;
}

ส่วนเรื่องการใช้งานใน Eigen ก็ต้องยอมรับว่าไม่ค่อยเข้าใจ

  • ทำไมพวกเขาต้องการ Eigen::aligned_allocator_indirection,
  • เหตุใดจึงต้องยกเว้นประเภทเลขคณิตใน EIGEN_WORKAROUND_MSVC_STL_SUPPORT
  • เหตุใดพวกเขาจึงต้องกำหนดตัวสร้างและตัวดำเนินการเหล่านี้ทั้งหมดใน Eigen::workaround_msvc_stl_support
  • หรือเหตุใดพวกเขาจึงต้องกำหนด resize ใหม่ในความเชี่ยวชาญเฉพาะทางบางส่วนของ std::vector สำหรับการจัดสรร Eigen::aligned_allocator_indirection...

ยินดีต้อนรับเบาะแส ประเด็นก็คือ เคล็ดลับนี้ใช้งานได้อย่างสมบูรณ์แบบ (เท่าที่ฉันสามารถบอกได้) และฉันไม่เห็นอะไรผิดปกติ นอกเหนือจากความไม่สง่างามเล็กน้อย

person François Beaune    schedule 23.02.2012

ตัวเลือกที่ง่ายที่สุด (และดีที่สุด imho) คือการจัดหาฟังก์ชันฟรีเป็นส่วนขยายของอินเทอร์เฟซ vector ซึ่งทำสิ่งที่ถูกต้อง ฟังก์ชั่นที่คุณต้องใช้ resize นั้นมีให้ใช้งานจากอินเทอร์เฟซสาธารณะของ std::vector:

#include <vector>

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size, T const& val)
{
  if (v.size() < new_size)
      v.insert(v.end(), new_size - v.size(), val);
  else if (new_size < v.size())
      v.erase(v.begin() + new_size, v.end());
}

และเพื่อความสอดคล้องกันก็มีเวอร์ชันอาร์กิวเมนต์เดียวด้วย:

template<class T, class Alloc>
void resize(std::vector<T, Alloc>& v,
    typename std::vector<T, Alloc>::size_type new_size)
{
    v.resize(new_size); // simply forward
}

อย่างไรก็ตาม หากคุณเพียงต้องการดรอปอินเวกเตอร์ใหม่และไม่ต้องกังวลกับฟังก์ชันฟรีหรือสมาชิก อีกทางเลือกหนึ่งก็คือคลาสย่อย std::vector:

#include <vector>
#include <memory>

template<class T, class Alloc = std::allocator<T>>
class myvector
  : public std::vector<T, Alloc>
{
  typedef std::vector<T, Alloc> base;
public:
  typedef typename base::size_type size_type;

  void resize(size_type new_size){
    base::resize(new_size);
  }

  void resize(size_type new_size, T const& val){
    if (this->size() < new_size)
        this->insert(this->end(), new_size - this->size(), val);
    else if (new_size < this->size())
        this->erase(this->begin() + new_size, this->end());
  }
};

โปรดทราบว่าฉันยังจัดเตรียมเวอร์ชันอาร์กิวเมนต์เดียวของ resize เนื่องจากเวอร์ชันอาร์กิวเมนต์ทั้งสองจะซ่อนเวอร์ชันคลาสฐานทั้งหมด โปรดทราบว่าฉันต้องเติมคำนำหน้าการเรียกฟังก์ชันสมาชิกทั้งหมดด้วย this-> เนื่องจากสิ่งเหล่านั้นขึ้นอยู่กับคลาสพื้นฐาน std::vector และด้วยอาร์กิวเมนต์เทมเพลต

person Xeo    schedule 23.02.2012
comment
การแบ่งคลาสย่อย std::vector เป็นหนึ่งในแนวทางแรกของฉัน แต่น่าเสียดายที่ยังคงล้มเหลวในการคอมไพล์เพราะในท้ายที่สุด std::vector ยังคงได้รับการสร้างอินสแตนซ์ - person François Beaune; 23.02.2012
comment
@ FrançoisBeaune: อ่า นั่นค่อนข้างโชคร้าย :/ ลองแสดงความคิดเห็นในเวอร์ชัน std::vector หรือเพียงแค่แก้ไขลายเซ็น :P FWIW, VS11 ยังคงไม่สามารถแก้ไขปัญหานี้ได้ - person Xeo; 23.02.2012
comment
แปลกตาม STL ที่นี่ vector::resize ใน VS11 ควรมีลายเซ็น T const& แต่ตัวอย่างสำหรับนักพัฒนาซอฟต์แวร์ในปัจจุบันไม่มี... ฉันคิดว่าฉันจะส่งอีเมลหาเขา - person Xeo; 23.02.2012