Bagaimana menangani perubahan desain untuk penghentian auto_ptr di C ++ 11?

12

Kami sedang menguji perpustakaan di bawah C ++ 11 (yaitu, -std=c++11). Perpustakaan menggunakan auto_ptrdan pola ini:

Foo* GetFoo()
{
    autoptr<Foo> ptr(new Foo);

    // Initialize Foo
    ptr->Initialize(...);

    // Now configure remaining attributes
    ptr->SomeSetting(...);

    return ptr.release();
}

C ++ 11 sudah usang auto_ptr, jadi kami ingin menjauh darinya.

Namun, kode ini mendukung C ++ 03 dan C ++ 11, jadi tidak sesederhana menarik auto_ptr. Perlu juga disebutkan bahwa perpustakaan tidak memiliki dependensi eksternal. Ini menggunakan C ++ 03; dan tidak menggunakan Autotools, Cmake, Boost, ...

Bagaimana seharusnya kita menangani perubahan desain untuk menjauh dari auto_ptrC ++ 11 sambil mempertahankan kompatibilitas dengan C ++ 03?

MetaFight
sumber
Apakah ada yang auto_ptrdicakup (yaitu std::auto_ptr), apakah mereka perlu atau dapatkah smart pointer diperoleh dari beberapa namespace lain?
Niall
Sebagai tambahan, Anda mungkin ingin melipat Foo::Initializeke dalam Foo::Foo.
MSalters
1
@ MSalters - ya, itu selalu menjadi salah satu hal yang saya merasa sedikit tidak nyaman. Perpustakaan dirancang pada 1990-an, dan saya pikir desainnya mirip dengan MFC. Artinya, ada konstruksi tingkat C ++ lebih rendah, dan kemudian konstruksi objek "tingkat lebih tinggi". Saya pikir fitur itu digunakan sebagai tradeoff sehingga kelas tidak memiliki 6 atau 12 konstruktor yang berbeda. (Pada titik ini, apa yang telah saya lakukan telah melalui dan memastikan variabel anggota dari tipe POD diinisialisasi ke default waras dalam konstruktor C ++).

Jawaban:

13

Dalam sebagian besar hal std::unique_ptrpenggantian dilakukan untuk menjadi pengganti (tetapi lebih aman) std::auto_ptr, sehingga harus ada sangat sedikit (jika ada) perubahan kode yang diperlukan selain (seperti yang Anda minta) mengarahkan kode untuk menggunakan salah satu unique_ptratau auto_ptr.

Ada beberapa cara untuk melakukan ini (dan masing-masing dilengkapi dengan daftar pengorbanan sendiri) di bawah ini. Mengingat contoh kode yang diberikan, saya akan memilih salah satu dari dua opsi pertama .

Pilihan 1

#if __cplusplus >= 201103L
template <typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif

Pengorbanan;

  • Anda memperkenalkan auto_ptrnama ke ruang nama global; Anda dapat mengurangi ini dengan mendefinisikannya sebagai namespace "pribadi" Anda sendiri
  • Setelah bermigrasi ke C ++ 17 (saya percaya auto_ptrakan sepenuhnya dihapus), Anda dapat lebih mudah mencari dan mengganti

pilihan 2

template <typename T>
struct my_ptr {
    #if __cplusplus >= 201103L
    typedef std::unique_ptr<T> ptr;
    #else
    typedef std::auto_ptr<T> ptr;
    #endif
};

Pengorbanan;

  • Mungkin lebih rumit untuk dikerjakan, semua auto_ptrkebutuhan saat ini diubah dalam kode menjadi sepertimy_ptr<T>::ptr
  • Keamanan yang lebih baik tidak diperkenalkan ke namespace global

Opsi 3

Agak kontroversial, tetapi jika Anda siap untuk menerima peringatan memiliki stdkelas sebagai basis

#if __cplusplus >= 201103L
template <typename T>
using my_ptr = std::unique_ptr<T>;
#else
template <typename T>
class my_ptr : public std::auto_ptr<T> {
  // implement the constructors for easier use
  // in particular
  explicit my_ptr( X* p = 0 ) : std::auto_ptr(p) {}
};
#endif

Pengorbanan;

  • Jangan mencoba menggunakan kelas yang diwariskan di mana basis virtual (khususnya wrt yang bukan-virtual destruktor) akan diharapkan. Bukan berarti ini harus menjadi masalah dalam kasus ini - tetapi sadari
  • Sekali lagi, perubahan kode
  • Ketidakcocokan namespace potensial - semuanya tergantung pada bagaimana kelas pointer digunakan untuk memulai

Opsi 4

Bungkus pointer dalam kelas baru dan agregasikan fungsi yang diperlukan untuk anggota

template <typename T>
class my_ptr { // could even use auto_ptr name?
  #if __cplusplus >= 201103L
  std::unique_ptr<T> ptr_;
  #else
  std::auto_ptr<T> ptr_;
  #endif

  // implement functions required...
  T* release() { return ptr_.release(); }
};

Pengorbanan;

  • Sedikit ekstrem ketika semua yang Anda inginkan adalah "menukar" implementasi keluar
Niall
sumber
Jawaban yang sangat bagus Saya sebenarnya sedikit meneliti, dan Anda mencapai setidaknya tiga tes yang saya coba. (Apa yang Anda kekurangan adalah OS X dan dentang hal-hal tertentu. OS X adalah beruang karena masih menggunakan TR1 namespace untuk C ++ 03 di kali, dan Anda harus memasukkan hal-hal menggunakan metode ini: Tidak ada jenis bernama 'unique_ptr' di namespace 'std' saat kompilasi di bawah LLVM / Dentang ).
@jww. Saya menggunakan OS X (XCode 6.4 dan Apple LLVM versi 6.1.0 (clang-602.0.53) (berdasarkan LLVM 3.6.0svn)) dan tidak memiliki masalah dengan campuran C ++ 03/11 selain tr1namespace no lagi di sana (saya menggunakan libc ++ dan bukan libstdc ++). Saya tahu tr1 adalah non-normatif, tetapi saya tidak dapat menemukan di mana pun dalam konsep (di sini) bahwa file harus <tr1/...>sama sekali, infact itu menyebutkan hanya berada di header <memory>dll file hanya di tr1namespace.
Niall
@jww. Saya kira memberikan campuran khusus dari kompiler, perpustakaan dan perangkat target - Anda mungkin perlu melakukan beberapa hand stand lagi. Selain itu, pada OS X, pertimbangkan untuk pindah ke dentang dan libc ++. Sejujurnya saya menganggap libc ++ sebagai pustaka C ++ "asli" yang baru untuk OS X - saya akan default untuk itu. Saya tidak punya cara untuk mendukung klaim-klaim ini selain bahwa sejarah dentang / hubungan Apple dan bahwa alat GCC pada OS X tampaknya ketinggalan zaman (perpustakaan) atau baru saja dihapus (sejauh yang saya tahu GCC adalah rintisan tipis untuk tetap berdentang pula ).
Niall
"Lain, pada OS X, pertimbangkan untuk pindah ke dentang dan libc ++ ..." - yeah, aku agak setuju denganmu. Namun, kami ingin membiarkan pengguna membuat pilihan itu, dan tidak memaksanya. (Mereka secara implisit membuat pilihan ketika mereka menentukan (atau kurang) CXX=...).
Berikut kasus yang menyebabkan saya begitu banyak masalah pada OS X 10.7 dan 10,8: c++ -v -std=c++11 -x c++ - < /dev/null. Saya grep'dmenyertakan direktori yang dibuang, dan mereka tidak termasuk unique_ptr.
0

Opsi 5: Alias ​​langsung.

#if __cplusplus >= 201103L
template<typename T> 
using MyPtr = std::unique_ptr<T>;
#else 
#define MyPtr std::auto_ptr
#endif 

Pengorbanan:

  1. Untuk versi bahasa yang lebih baru, AKA C ++ 11 dan yang lebih baru, alias Anda ketik memetakan ke smart pointer yang benar. Kode pengguna apa pun yang sebenarnya bergantung pada API khusus untuk std :: auto_ptr akan ditandai oleh kompiler, yang merupakan jaminan utama bahwa itu akan benar-benar diperbaiki.

  2. Dalam mode Legacy c ++ 03 jenis alias adalah makro. Ini kasar, tetapi sintaks yang dihasilkan MyPtr<T>akan identik dengan kasus C ++ 11 sepanjang sisa kode.

  3. Anda harus menemukan dan mengubah semua variabel auto_ptr Anda MyPtruntuk mengaturnya.

pengguna3726672
sumber
1
Sangat tidak jelas apa ini referensi (dan, seperti yang diungkapkan, ini bukan pertanyaan sama sekali).
autophage
1
@ autophage Saya percaya ini adalah jawaban ... jadi mungkin bukan pertanyaan.
Kain0_0