나는 "스스로해야한다"는 명백한 해결책을 반복 할 것이다. 이것은 간단한 클래스와 클래스 템플릿 모두에서 작동하는 간결한 C ++ 11 버전의 코드입니다.
#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")
ideone 에서 실제 동작을 볼 수 있습니다 . 이 결과로 이어지는 기원은 다음과 같습니다.
#define DECLARE_SELF(Type) typedef Type _TySelf; /**< @brief type of this class */
struct XYZ {
DECLARE_SELF(XYZ)
};
이것은 코드를 다른 클래스에 복사하여 붙여넣고 XYZ를 변경하는 것을 잊어 버리는 명백한 문제가 있습니다.
struct ABC {
DECLARE_SELF(XYZ) // !!
};
내 첫 번째 접근 방식은 그다지 독창적이지 않았습니다. 다음과 같은 함수를 만들었습니다.
/**
* @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
다소 길지만 여기서는 참아주세요. 이 함수는 .NET Framework 형식을 추론하는 데 사용 decltype되므로 C ++ 03 __self_check_helper에서 this. 또한는 static_assert없지만 sizeof()대신 트릭이 사용됩니다. C ++ 0x의 경우 훨씬 더 짧게 만들 수 있습니다. 이제 이것은 템플릿에서 작동하지 않습니다. 또한, 매크로 현학적으로 컴파일 할 경우, 그것은 여분의 불필요한 세미콜론에 대해 불평한다 (또는 홀수 찾고 매크로의 본문에 세미콜론으로 끝나지 않는 남아있을 것입니다, 마지막에 세미콜론을 기대하지 않고있는 사소한 문제가 XYZ및 ABC).
Type전달 된 에 대해 확인 하는 것은 DECLARE_SELF옵션이 아닙니다 . 이는 오류 XYZ가 있는 클래스를 확인 하지 않고 ABC( 괜찮음) 클래스 만 확인하기 때문 입니다. 그리고 그것은 나를 때렸다. 템플릿과 함께 작동하는 추가 스토리지 제로 비용 솔루션 :
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
이것은 단순히 고유 한 열거 형 값 (또는 한 줄에 모든 코드를 작성하지 않는 경우 적어도 고유 한 경우)에 대한 정적 주장을 만들고, 유형 비교 속임수를 사용하지 않으며 템플릿에서도 정적 주장으로 작동합니다. . 그리고 보너스로-이제 마지막 세미콜론이 필요합니다 :).
좋은 영감을 주신 Yakk에게 감사드립니다. 나는 그의 대답을 먼저 보지 않고 이것을 쓰지 않을 것입니다.
VS 2008 및 g ++ 4.6.3으로 테스트되었습니다. 실제로 XYZ및 ABC예제를 사용하면 다음 과 같이 불평합니다.
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>â
이제 ABC를 템플릿으로 만들면 :
template <class X>
struct ABC {
DECLARE_SELF(XYZ); // line 92
};
int main(int argc, char **argv)
{
ABC<int> abc;
return 0;
}
우리는 얻을 것이다:
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>â
함수 검사가 (예상대로) 컴파일되지 않았기 때문에 행 번호 검사 만 트리거되었습니다.
C ++ 0x (그리고 악한 밑줄없이)를 사용하려면 다음이 필요합니다.
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
CStaticAssert 비트는 템플릿 본문에서 typedef-ed 된 유형을 생성하므로 유감스럽게도 여전히 필요하다고 생각합니다 (으로 동일하게 할 수 없다고 가정합니다 static_assert). 이 접근 방식의 장점은 여전히 비용이 전혀 들지 않는다는 것입니다.
this_t아마도 일반 C ++ 명명과 더 잘 맞을 것입니다.