C ++ 에는 PHP의 키워드 와 동등한 기능 이 없습니다 .self
둘러싸는 클래스의 유형으로 평가되는 .
클래스별로 위조하는 것은 쉽습니다.
struct Foo
{
typedef Foo self;
};
그러나 나는 Foo
다시 써야 했다. 언젠가는 이것이 잘못되어 조용한 버그를 일으킬 수 있습니다.
decltype
이 작업을 “자율적으로”만들기 위해 및 친구들의 조합을 사용할 수 있습니까 ? 나는 이미 다음을 시도 하지만 this
그 곳에서는 유효하지 않습니다 :
struct Foo
{
typedef decltype(*this) self;
};
// main.cpp:3:22: error: invalid use of 'this' at top level
// typedef decltype(*this) self;
( static
동일한 작업을 수행하지만 후기 바인딩을 사용 하는)에 대해서는 걱정하지 않을 것 입니다.
답변
다음은 Foo 유형을 반복하지 않고 수행 할 수있는 방법입니다.
template <typename...Ts>
class Self;
template <typename X, typename...Ts>
class Self<X,Ts...> : public Ts...
{
protected:
typedef X self;
};
#define WITH_SELF(X) X : public Self<X>
#define WITH_SELF_DERIVED(X,...) X : public Self<X,__VA_ARGS__>
class WITH_SELF(Foo)
{
void test()
{
self foo;
}
};
에서 파생 하려면 다음과 같은 방식으로 Foo
매크로 WITH_SELF_DERIVED
를 사용해야합니다 .
class WITH_SELF_DERIVED(Bar,Foo)
{
/* ... */
};
원하는만큼 많은 기본 클래스를 사용하여 다중 상속을 수행 할 수도 있습니다 (가변 템플릿 및 가변 매크로 덕분에).
class WITH_SELF(Foo2)
{
/* ... */
};
class WITH_SELF_DERIVED(Bar2,Foo,Foo2)
{
/* ... */
};
gcc 4.8 및 clang 3.4에서 작동하는지 확인했습니다.
답변
가능한 해결 방법 (유형을 한 번만 작성해야하므로) :
template<typename T>
struct Self
{
protected:
typedef T self;
};
struct Foo : public Self<Foo>
{
void test()
{
self obj;
}
};
더 안전한 버전을 위해 T
실제로 다음에서 파생 된다는 것을 확인할 수 있습니다 Self<T>
.
Self()
{
static_assert(std::is_base_of<Self<T>, T>::value, "Wrong type passed to Self");
}
• 그래도주의 static_assert
멤버 함수 내부 아마 유형에 전달로 확인하는 유일한 방법입니다 std::is_base_of
완료해야합니다.
답변
정규 클래스 선언 대신 매크로를 사용할 수 있습니다.
#define CLASS_WITH_SELF(X) class X { typedef X self;
그리고 다음과 같이 사용하십시오.
CLASS_WITH_SELF(Foo)
};
#define END_CLASS };
가독성에 도움이 될 것입니다.
@Paranaix를 가져 와서 Self
사용할 수도 있습니다 (정말 hackish가 시작됩니다)
#define WITH_SELF(X) X : public Self<X>
class WITH_SELF(Foo) {
};
답변
긍정적 인 증거는 없지만 불가능하다고 생각 합니다. 다음은 실패합니다 – 당신의 시도와 같은 이유로 – 그리고 저는 그것이 우리가 얻을 수있는 가장 먼 것이라고 생각합니다.
struct Foo {
auto self_() -> decltype(*this) { return *this; }
using self = decltype(self_());
};
본질적으로 이것이 보여주는 것은 typedef를 선언하려는 범위가 단순히에 대한 액세스 권한 이 없고 (직접적이든 간접적이든) this
클래스의 유형이나 이름을 얻는 다른 (컴파일러 독립적) 방법이 없다는 것입니다.
답변
GCC와 clang 모두에서 작동 하는 것은 함수 typedef의 후행 반환 유형에서 this
사용하여 참조하는 typedef를 만드는 것 this
입니다. 이것은 정적 멤버 함수의 선언이 아니기 때문에의 사용 this
이 허용됩니다. 그런 다음 해당 typedef를 사용하여 self
.
#define DEFINE_SELF() \
typedef auto _self_fn() -> decltype(*this); \
using self = decltype(((_self_fn*)0)())
struct Foo {
DEFINE_SELF();
};
struct Bar {
DEFINE_SELF();
};
불행히도 표준을 엄격하게 읽으면 이것이 유효하지 않다고 말합니다. clang이하는 일은 this
정적 멤버 함수의 정의에 사용되지 않는 것을 확인하는 것 입니다. 그리고 여기에서는 실제로 그렇지 않습니다. GCC는 this
함수의 종류에 관계없이 후행 반환형으로 사용 되어도 상관 없으며, static
멤버 함수 에서도 가능합니다 . 그러나 표준이 실제로 요구하는 것은this
것은 비 정적 멤버 함수 (또는 비 정적 데이터 멤버 이니셜 라이저)의 정의 외부에서 사용되지 않는다는 것입니다. 인텔은이를 제대로 받아들이고이를 거부합니다.
을 고려하면:
this
비 정적 데이터 멤버 이니셜 라이저 및 비 정적 멤버 함수 ([expr.prim.general] p5)에서만 허용됩니다.- 비 정적 데이터 멤버는 이니셜 라이저 ([dcl.spec.auto] p5)에서 유형을 추론 할 수 없습니다.
- 비 정적 멤버 함수는 함수 호출 컨텍스트에서 정규화되지 않은 이름으로 만 참조 될 수 있습니다 ([expr.ref] p4).
- 비 정적 멤버 함수는 평가되지 않은 컨텍스트에서도 정규화되지 않은 이름으로 만 호출 될
this
수 있습니다 ([over.call.func] p3), - 정규화 된 이름 또는 멤버 액세스로 비 정적 멤버 함수에 대한 참조에는 정의중인 형식에 대한 참조가 필요합니다.
self
어떤 식 으로든 어딘가에 형식 이름을 포함하지 않고는 구현할 방법이 전혀 없다고 결론적으로 말할 수 있다고 생각 합니다.
편집 : 이전 추론에 결함이 있습니다. “비 정적 멤버 함수는 평가되지 않은 컨텍스트에서도 사용할 수있는 경우 ([over.call.func] p3) 정규화되지 않은 이름으로 만 호출 할 수 있습니다.”가 잘못되었습니다. 그것은 무엇 실제로 말한다 것은
키워드
this
(9.3.2)가 범위 내에 있고 classT
또는 파생 클래스T
를 참조하는 경우 암시 된 개체 인수는(*this)
입니다. 키워드this
가 범위에 없거나 다른 클래스를 참조하는 경우 인위적인 유형의 개체T
가 암시 된 개체 인수가됩니다. 인수 목록이 인위적인 객체에 의해 증가되고 오버로드 해결이의 비 정적 멤버 함수 중 하나를 선택T
하면 호출 형식이 잘못됩니다.
정적 멤버 함수 내부에는 this
나타나지 않을 수 있지만 여전히 존재합니다.
그러나 주석에 따라 정적 멤버 함수 내에서 f()
to 의 변환 (*this).f()
이 수행되지 않고 수행되지 않으면 [expr.call] p1이 위반됩니다.
[…] 멤버 함수 호출의 경우 접미사 식은 암시 적 (9.3.1, 9.4) 또는 명시 적 클래스 멤버 액세스 (5.2.5) 여야하며 […]
회원 액세스 권한이 없기 때문입니다. 그래서 그것조차 작동하지 않을 것입니다.
답변
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T)
struct Foo {
SELF(Foo); // works, self is defined as `Foo`
};
struct Bar {
SELF(Foo); // fails
};
이것은 self_check
호출되지 않은 템플릿 유형에서 작동하지 static_assert
않으므로 평가되지 않습니다.
template
s에서도 작동하도록 몇 가지 해킹을 할 수 있지만 실행 시간이 약간 소요됩니다.
#define TESTER_HELPER_TYPE \
template<typename T, std::size_t line> \
struct line_tester_t { \
line_tester_t() { \
static_assert( std::is_same< decltype(T::line_tester), line_tester_t<T,line> >::value, "test failed" ); \
static_assert( std::is_same< decltype(&T::static_test_zzz), T*(*)() >::value, "test 2 failed" ); \
} \
}
#define SELF_CHECK( SELF ) void self_check() { static_assert( std::is_same< typename std::decay<decltype(*this)>::type, SELF >::value, "self wrong type" ); }
#define SELF(T) typedef T self; SELF_CHECK(T); static T* static_test_zzz() { return nullptr; }; TESTER_HELPER_TYPE; line_tester_t<T,__LINE__> line_tester
빈 struct
크기가 1 바이트는 클래스에 생성됩니다. 유형이 인스턴스화되면 self
테스트 대상입니다.
답변
나는 또한 불가능하다고 생각합니다. 여기에 또 다른 실패했지만 IMHO 흥미로운 시도가 있습니다 this
.
template<typename T>
struct class_t;
template<typename T, typename R>
struct class_t< R (T::*)() > { using type = T; };
struct Foo
{
void self_f(); using self = typename class_t<decltype(&self_f)>::type;
};
#include <type_traits>
int main()
{
static_assert( std::is_same< Foo::self, Foo >::value, "" );
}
C ++ self_f
에서 주소를 가져오고 싶을 때 클래스 로 자격을 부여해야하기 때문에 실패합니다.