Saya akan mengulangi solusi yang jelas "harus melakukannya sendiri". Ini adalah versi kode C ++ 11 yang ringkas, yang berfungsi dengan kelas sederhana dan template kelas:
#define DECLARE_SELF(Type) \
typedef Type TySelf; /**< @brief type of this class */ \
/** checks the consistency of TySelf type (calling it has no effect) */ \
void self_check() \
{ \
static_assert(std::is_same<decltype(*((TySelf*)(0))), \
decltype(*this)>::value, "TySelf is not what it should be"); \
} \
enum { static_self_check_token = __LINE__ }; \
static_assert(int(static_self_check_token) == \
int(TySelf::static_self_check_token), \
"TySelf is not what it should be")
Anda dapat melihatnya beraksi di ideone . Asal, yang mengarah ke hasil ini di bawah:
#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */
struct XYZ {
DECLARE_SELF(XYZ)
};
Ini memiliki masalah yang jelas dengan menyalin-menempel kode ke kelas yang berbeda dan lupa mengubah XYZ, seperti di sini:
struct ABC {
DECLARE_SELF(XYZ) // !!
};
Pendekatan pertama saya tidak terlalu orisinal - membuat fungsi, seperti ini:
/**
* @brief namespace for checking the _TySelf type consistency
*/
namespace __self {
/**
* @brief compile-time assertion (_TySelf must be declared the same as the type of class)
*
* @tparam _TySelf is reported self type
* @tparam _TyDecltypeThis is type of <tt>*this</tt>
*/
template <class _TySelf, class _TyDecltypeThis>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;
/**
* @brief compile-time assertion (specialization for assertion passing)
* @tparam _TySelf is reported self type (same as type of <tt>*this</tt>)
*/
template <class _TySelf>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TySelf, _TySelf> {};
/**
* @brief static assertion helper type
* @tparam n_size is size of object being used as assertion message
* (if it's a incomplete type, compiler will display object name in error output)
*/
template <const size_t n_size>
class CStaticAssert {};
/**
* @brief helper function for self-check, this is used to derive type of this
* in absence of <tt>decltype()</tt> in older versions of C++
*
* @tparam _TyA is reported self type
* @tparam _TyB is type of <tt>*this</tt>
*/
template <class _TyA, class _TyB>
inline void __self_check_helper(_TyB *UNUSED(p_this))
{
typedef CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<_TyA, _TyB>)> _TyAssert;
// make sure that the type reported as self and type of *this is the same
}
/**
* @def __SELF_CHECK
* @brief declares the body of __self_check() function
*/
#define __SELF_CHECK \
/** checks the consistency of _TySelf type (calling it has no effect) */ \
inline void __self_check() \
{ \
__self::__self_check_helper<_TySelf>(this); \
}
/**
* @def DECLARE_SELF
* @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
* @param[in] Type is type of the enclosing class
*/
#define DECLARE_SELF(Type) \
typedef Type _TySelf; /**< @brief type of this class */ \
__SELF_CHECK
} // ~self
Agak panjang, tapi tolong bersabarlah dengan saya di sini. Ini memiliki keuntungan bekerja di C ++ 03 tanpa decltype, karena __self_check_helperfungsi tersebut digunakan untuk menyimpulkan jenis this. Juga, tidak ada static_assert, tetapi sizeof()trik yang digunakan sebagai gantinya. Anda bisa membuatnya lebih pendek untuk C ++ 0x. Sekarang ini tidak akan berfungsi untuk template. Juga, ada masalah kecil dengan makro yang tidak mengharapkan titik koma di akhir, jika dikompilasi dengan pedantic, itu akan mengeluh tentang titik koma ekstra yang tidak perlu (atau Anda akan ditinggalkan dengan makro yang tampak aneh tidak berakhir dengan titik koma di badan XYZdan ABC).
Melakukan pemeriksaan pada Typeyang diteruskan DECLARE_SELFbukanlah suatu pilihan, karena itu hanya akan memeriksa XYZkelas (yang ok), tidak menyadari ABC(yang memiliki kesalahan). Dan kemudian aku tersadar. Solusi penyimpanan tanpa biaya tambahan yang bekerja dengan template:
namespace __self {
/**
* @brief compile-time assertion (_TySelf must be declared the same as the type of class)
* @tparam b_check is the asserted value
*/
template <bool b_check>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2;
/**
* @brief compile-time assertion (specialization for assertion passing)
*/
template <>
class CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<true> {};
/**
* @def DECLARE_SELF
* @brief declares _TySelf type and adds code to make sure that it is indeed a correct one
* @param[in] Type is type of the enclosing class
*/
#define DECLARE_SELF(Type) \
typedef Type _TySelf; /**< @brief type of this class */ \
__SELF_CHECK \
enum { __static_self_check_token = __LINE__ }; \
typedef __self::CStaticAssert<sizeof(CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<int(__static_self_check_token) == int(_TySelf::__static_self_check_token)>)> __static_self_check
} // ~__self
Ini hanya membuat pernyataan statis pada nilai enum unik (atau setidaknya unik jika Anda tidak menulis semua kode Anda pada satu baris), tidak ada tipu daya pembanding tipe yang digunakan, dan berfungsi sebagai pernyataan statis, bahkan di template . Dan sebagai bonus - titik koma terakhir sekarang diperlukan :).
Saya ingin berterima kasih kepada Yakk karena telah memberi saya inspirasi yang baik. Saya tidak akan menulis ini tanpa melihat jawabannya terlebih dahulu.
Diuji dengan VS 2008 dan g ++ 4.6.3. Memang, dengan contoh XYZdan ABCitu mengeluh:
ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp:91:5: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
self.cpp:91:5: error: template argument 1 is invalid
self.cpp: In function âvoid __self::__self_check_helper(_TyB*) [with _TyA = XYZ, _TyB = ABC]â:
self.cpp:91:5: instantiated from here
self.cpp:58:87: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<XYZ, ABC>â
Sekarang jika kita membuat template ABC:
template <class X>
struct ABC {
DECLARE_SELF(XYZ); // line 92
};
int main(int argc, char **argv)
{
ABC<int> abc;
return 0;
}
Kita akan mendapatkan:
ipolok@ivs:~$ g++ self.cpp -c -o self.o
self.cpp: In instantiation of âABC<int>â:
self.cpp:97:18: instantiated from here
self.cpp:92:9: error: invalid application of âsizeofâ to incomplete type â__self::CSELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE2<false>â
Hanya pemeriksaan nomor baris yang dipicu, karena pemeriksaan fungsi tidak dikompilasi (seperti yang diharapkan).
Dengan C ++ 0x (dan tanpa garis bawah jahat), Anda hanya perlu:
namespace self_util {
/**
* @brief compile-time assertion (tokens in class and TySelf must match)
* @tparam b_check is the asserted value
*/
template <bool b_check>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE;
/**
* @brief compile-time assertion (specialization for assertion passing)
*/
template <>
class SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<true> {};
/**
* @brief static assertion helper type
* @tparam n_size is size of object being used as assertion message
* (if it's a incomplete type, compiler will display object name in error output)
*/
template <const size_t n_size>
class CStaticAssert {};
#define SELF_CHECK \
/** checks the consistency of TySelf type (calling it has no effect) */ \
void self_check() \
{ \
static_assert(std::is_same<TySelf, decltype(*this)>::value, "TySelf is not what it should be"); \
}
#define DECLARE_SELF(Type) \
typedef Type TySelf; /**< @brief type of this class */ \
SELF_CHECK \
enum { static_self_check_token = __LINE__ }; \
typedef self_util::CStaticAssert<sizeof(SELF_TYPE_MUST_BE_THE_SAME_AS_CLASS_TYPE<int(static_self_check_token) == int(TySelf::static_self_check_token)>)> static_self_check
} // ~self_util
Saya percaya bahwa bit CStaticAssert sayangnya masih diperlukan karena menghasilkan sebuah tipe, yang diketikkan dalam tubuh template (saya kira hal yang sama tidak dapat dilakukan dengan static_assert). Keuntungan dari pendekatan ini masih biaya nolnya.
this_tmungkin lebih selaras dengan penamaan C ++ biasa.