ProgramingTip

ʻint foo :: * bar :: *`는 무엇입니까?

bestdevel 2020. 11. 5. 08:21
반응형

ʻint foo :: * bar :: *`는 무엇입니까?


C ++의 멋진 점은 멤버에 대한 포인터 유형의 변수를 만들 수있을 것입니다. 가장 일반적인 사용 사례는 메서드에 대한 포인터를 얻는 것입니다.

struct foo
{
    int x() { return 5; }
};

int (foo::*ptr)() = &foo::x;
foo myFoo;
cout << (myFoo.*ptr)() << '\n'; // prints "5"

그러나 뒤죽박죽으로 나는 그들이 멤버 변수를 가리킬 수 있다고 생각합니다.

struct foo
{
    int y;
};

int foo::*ptr = &foo::y;
foo myFoo;
myFoo.*ptr = 5;
cout << myFoo.y << '\n'; // prints "5"

이것은 꽤 rad입니다. 그것은 나를 추가 실험으로 이끌었습니다. 구조의 하위 멤버에 대한 포인터를 소유 수있는 직원 어떨까요?

struct foo
{
    int y;
};

struct bar
{
    foo aFoo;
};

int bar::*foo::*ptr;

이것은 실제로 실행 됩니다.

그러나 유용한 것을 할당하는 방법을 많이. 다음 중 어느 것도 작동하지 않습니다.

int bar::*foo::*ptr = &bar::foo::y; // no member named "foo" in "bar"
int bar::*foo::*ptr = &bar::aFoo::y; // no member named "aFoo" in "bar" (??)
int bar::*foo::*ptr = &foo::y; // can't init 'int bar::*foo::*' with 'int foo::*'

또한 이것이 생성하는 오류에 따르면 유형은 내가 염두에 둔 것과 일치하지 않는 것과 일치하지 않습니다.

int bar::*foo::*ptr = nullptr;
bar myBar;
myBar.*ptr = 4; // pointer to member type ‘int bar::*’ incompatible
                // with object type ‘bar’

이 개념은 나를 피하는 것 가변. 분명히, 내가 기대하는 것과 완전히 다른 방식으로 구문 분석을 배제 할 수 없습니다.

누군가가 int bar::*foo::*실제로 무엇 인지 설명해 주시겠습니까 ? GCC 멤버에, 대한 가의 포인터가 object- bar와 호환되지 않는다고 알려주는 이유는 무엇 bar입니까? 를 어떻게 사용 int bar::*foo::*합니까?


"유효한"방법은 초기화하는 괴물을 초기화합니다.

struct bar;

struct foo
{
    int y;    
    int bar::* whatever;
};

struct bar
{
    foo aFoo;
};

int bar::* foo::* ptr = &foo::whatever;

보시다시피, ptrfoo( foo::*, 오른쪽에서 왼쪽으로 읽는) 멤버에 대한 포인터이며 , 여기서 해당 멤버는 그 멤버가 int bar( bar::*) 의 멤버에 대한 포인터 입니다.

int bar :: * foo :: *를 어떻게 사용합니까?

당신은 그렇게하지 않을 것입니다. 그러나 당신이 협박을 받고 계시다면 시도하십시오!

struct bar
{
    foo aFoo;

    int really;
};

int bar::* foo::* ptr = &foo::whatever;
foo fleh;
fleh.whatever = &bar::really;
bar blah;
blah.*(fleh.*ptr) = 42;
std::cout << blah.really << std::endl;

데이터 멤버 (의 int멤버 bar)에 대한 포인터 인 데이터 멤버에 대한 포인터 입니다.

실제로 도움이 무엇인지 묻지 머리가 약간 회전합니다. :)

편집 : 여기에 실제 동작의 전체 예가 있습니다.

#include <iostream>

struct bar {
    int i;
};

struct foo {
    int bar::* p;
};

int main()
{
    bar b;
    b.i = 42;

    foo f;
    f.p = &bar::i;

    int bar::*foo::*ptr = &foo::p;
    std::cout << (b.*(f.*ptr));
}

물론 출력은 42입니다.

훨씬 더 재미있을 수 있습니다. 여기 멤버 함수에 대한 포인터를 반환하는 멤버 함수에 대한 포인터가 있습니다.

#include <iostream>

struct bar {
    int f_bar(int i) { return i; };
};

struct foo {
    int(bar::*f_foo())(int)
    {
        return &bar::f_bar;
    }
};

int main()
{
    int(bar::*((foo::*ptr)()))(int) = &foo::f_foo;

    bar b;
    foo f;

    std::cout << (b.*((f.*ptr)()))(42);
}


선언을 파싱 해 봅시다 int bar::*foo::*ptr;.

§8.3.3 [dcl.mptr] / p1 :

선언에서 형태를T DD

nested-name-specifier * attribute-specifier-seq_opt cv-qualifier-seq_opt D1

이름 지정자는 중첩 클래스를 나타내며 선언의 식별자 유형 T D1은 " 파생 선언자 유형 목록 T "경우 식별자 유형 인 D은 " 파생 선언자 유형 목록 CV-규정입니다. -seq " 유형 의 클래스 중첩 이름 지정자 멤버에, 대한 포인터 T.

  • 1 단계 : 이것은 T= int, nested-name-specifier = bar::, 및 에서 위 형식의 선언입니다 D1 = foo::* ptr. 먼저 선언 T D1또는 int foo::* ptr.

  • 2 단계 : 동일한 규칙을 다시 적용합니다. = int, nested-name-specifier = = 인 int foo::* ptr위 형식의 선언입니다 . 분명히의 식별자 유형 은 " "이므로 선언 의 식별자 유형 은 " 유형 클래스 멤버에 대한 포인터 "입니다.Tfoo::D1ptrint ptrintptrint foo::* ptrfooint

  • 3 단계. 원래 선언으로 돌아갑니다. T D1( int foo::* ptr) 의 식별자 유형은 2 단계에서 " 클래스 foo유형의 멤버에 대한 포인터 int"이므로 파생 된 선언자 유형 목록 은 "클래스 foo유형의 멤버에 대한 포인터 "입니다. 대체이 선언이 선언라고 우리에게 이야기한다 ptr"클래스의 멤버에 대한 포인터로 foo클래스의 멤버로 유형 포인터의 bar유형 int".

바라건대 당신은 그런 괴물을 사용할 필요가 없을 것입니다.


누군가 궁금해하는 경우 여러 레이어를 중첩하는 멤버에 대한 포인터를 만들 수 없습니다. 그 이유는 모든 멤버에 대한 포인터가 실제로는 처음 보는 것보다 훨씬 더 복잡하기 때문입니다. 그들은 단순히 특정 멤버에 대한 특정 오프셋을 포함하지 않습니다.

가상 상속 등으로 인해 단순 오프셋을 사용하는 것은 작동하지 않습니다. 기본적으로 단일 유형 내에서도 특정 필드의 오프셋이 인스턴스마다 다르므로 런타임에 멤버에 대한 포인터 확인을 수행해야합니다. 대부분 이것은 표준이 비 POD 유형의 내부 레이아웃이 작동하는 방식을 지정하지 않았기 때문에 정적으로 작동하도록 만들 방법이 없기 때문입니다.

이 경우 일반 포인터 대 멤버로는 2 레벨 심층 분석을 수행 할 수 없지만 컴파일러는 멤버 대 한 심층 포인터 정보의 두 배를 포함하도록 포인터를 생성해야합니다. .

멤버에 대한 포인터가 그다지 일반적이지 않기 때문에 동일한 결과를 얻기 위해 여러 포인터를 사용할 수있는 경우 다중 레이어 딥 멤버를 설정할 수있는 구문을 실제로 만들 필요가 없다고 생각합니다.


먼저 "가독성"을 높이기 위해 괄호를 사용할 수 있습니다 (컴파일이 작동 함).

struct bar;

struct foo
{
    int y;
    int (bar:: *whatever); // whatever is a pointer upon an int member of bar.
};

struct bar
{
    foo aFoo;
};

// ptr is a pointer upon a member of foo which points upon an int member of bar.
int (bar:: *(foo:: *ptr)) = &foo::whatever;

참고

int (bar :: * 무엇이든)

다음과 같다

int (* 무엇이든)

바 회원에 대한 제약이 있습니다.

에 관해서

int (bar :: * (foo :: * ptr))

, 그것은

정수 (* (* ptr))

foo와 bar의 멤버십에 대한 두 가지 제약이 있습니다.

그것들은 단지 포인터입니다. bar 또는 foo에 실제로 호환 가능한 멤버가 있는지 확인하지 않습니다. 이는 클래스 bar의 포워드 선언을 사용하지 못하게하고 클래스 bar는 다른 클래스가 포인터를 통해 멤버를 참조하는지 확인하지 않기 때문입니다. 또한 불투명 한 클래스를 참조해야 할 수도 있습니다 (즉, 별도의 단위에 정의 된 클래스 막대가 있음).

유용성은 어떻습니까? 클래스 래퍼를 통해 클래스 멤버의 값을 설정 / 가져 오는 방법으로 C ++ 리플렉션을 사용할 수 있습니까?

template< typename Class, typename Type >
struct ClassMember
{
    using MemberPointer = Type (Class:: *);
    MemberPointer member;
    ClassMember(MemberPointer member) : member(member) {}
    void operator()(Class & object, Type value) { object.*member = value; }
    Type operator()(Class & object)  { return object.*member; }
};

template< typename Class, typename Type > ClassMember< Class, Type > MakeClassMember(Type(Class:: *member))
{
    return ClassMember< Class, Type >(member);
}

struct Rectangle
{
    double width;
    double height;

    Rectangle(double width = 0., double height = 0.) : width(width), height(height) {}
};

int main(int argc, char const *argv[])
{
    auto r = Rectangle(2., 1.);

    auto w = MakeClassMember(&Rectangle::width);
    auto h = MakeClassMember(&Rectangle::height);

    w(r, 3.);
    h(r, 2.);

    printf("Rectangle(%f, %f)\n", w(r), h(r));

    return 0;
}

물론,이 예제에서는 이중 멤버 포인터의 특정 사용법을 보여주지 않습니다. 여기에 간단한 설명 방법이나 개념적으로 설명해야하는 좋은 이유가 없기 때문입니다.

참고 URL : https://stackoverflow.com/questions/26200491/what-is-an-int-foobar

반응형