이 질문은 약간의 논쟁의 원인으로 보이므로, 나는 가상의 구문으로 의도를 보여주고 구현을 보여주기 위해 편집 중입니다. 구현은 놀랍도록 타입 캐스트와이 타입 캐스트 된 포인터의 호출에 의존합니다. 문제는 형식 캐스트가 표준 (C++이긴하지만)이지만 결과를 호출하는 것은 정의되지 않은 동작입니다. 내 질문에 대한 표준 최근에 있거나 형식 캐스트 멤버 함수 포인터를 더 이상 정의되지 않은 동작을 호출 한 결과를 곧 변경할지 여부를 우려합니다. 멤버 함수 포인터가 가리키는대로 우리는 객체가 같은 클래스의 인스턴스가 될 "프로그래머"이라고 가정C++ 14 또는 C++ 1z에서 대리자 클래스 멤버 함수 포인터를 호출하기 위해 더 이상 정의되지 않았습니까?
void* object = ...; universal_mf_ptr mf_ptr = ...;
reinterpret_call(object, mf_ptr);
:
목적은 같은 코드를 쓸 수있다 . 그러나 클래스 유형은 호출 사이트에서 "컴파일러에"알려지지 않습니다. 형식 universal_mf_ptr
은 "모든 클래스 형식의 멤버 함수에 대한 포인터"의 자리 표시 자입니다. reinterpret_call
은 컴파일러에게 "이 호출은 런타임에 유효 할 것입니다. 스택에서 객체의 주소를 누르고 호출하여 간접 mf_ptr을 호출하는 어셈블리 명령어를 내 보냅니다."라는 사실을 가정하는 구문입니다. 이것은 컴파일러에게 "신뢰해라,이 캐스트는 런타임에 유효하다. 그냥 캐스트를한다."라고 비유하여 reinterpret_cast
과 비슷한 이름을 붙였다.
놀랍게도 universal_mf_ptr
은 진짜이며 표준에서는 정의되지 않은 동작이 아닙니다. (아래의 링크 된 기사에 따르면) 멤버 함수 포인터는 다른 멤버 함수 포인터 (다른/호환되지 않는 클래스 유형의 경우에도)에 reinterpret_cast 될 수 있습니다. 그러나 표준 인이지만 휴대용이 아닙니다 (즉 모든 컴파일러가 표준의이 부분을 구현하지는 않습니다).
정의되지 않은 동작은 실제로 reinterpret_cast
의 ed 멤버 함수 포인터를 사용 (호출)하려고 할 때 사용됩니다. 이것은 표준 인에 따라 정의되지 않은 동작이지만 링크 된 아티클에 따라 관련없는 클래스 유형에 대한 멤버 함수 포인터를 캐스팅하는 (이식 가능하지 않지만 표준이지만) 기능을 구현하는 모든 컴파일러에서 구현됩니다. 작성자의 주장은 포인터를 표준으로 캐스팅하면 캐스팅 된 포인터를 호출해야한다는 것입니다.
임의의 경우, 하나의 컬렉션에 이기종 멤버 함수를 저장하는 것과 같이 범용 멤버 함수 포인터 유형에 대한 캐스팅 멤버 함수 포인터의 (표준, 미정도는 아니지만 이식 가능하지 않은) 기능을 활용해야 할 경우 , "희생자"클래스를 타입 캐스트의 대상으로 임의로 지정해야합니다. 이 클래스는 멤버 함수를 가질 필요가 없습니다. 실제로 멤버가 없거나 선언 된 채로 선언되지 않고 정의되지 않은 상태 일 수 있습니다.
나는 victim 클래스를 임의로 선택하고 멤버 함수 포인터가 실제로 멤버가 아닌 클래스라고 주장하는 것이이 질문의 원인이 무엇인지 의심 스럽습니다. 많은 사람들이 이런 식으로 표준이 될 수는 없으며 표준이되어서는 안되기 때문에 멤버 함수를 사용하면 캐스트도 똑같이 적용될 수 있습니다. 그러나 후자는 이미 표준에 있습니다.
기술은 described in this article이지만 경고 :
멤버 함수 포인터간에 매우 어두운 영역을 주조한다.C++의 표준화 중에 한 클래스의 멤버 함수 포인터를 기본 클래스 나 파생 클래스의 멤버 함수 포인터로 캐스팅 할 수 있어야하는지, 관련없는 클래스간에 캐스트 할 수 있는지 여부에 대한 논의가 많이있었습니다. 표준위원회가 마음을 굳히기 전까지는 여러 컴파일러 공급 업체가 이미 구현 결정을 내 렸으며 이러한 결정을 통해 이러한 질문에 대한 다른 대답을 얻을 수있었습니다. 표준 (5.2.10/9 절)에 따르면 reinterpret_cast를 사용하여 관련없는 클래스의 멤버 함수 포인터 안에 한 클래스의 멤버 함수를 저장할 수 있습니다. casted 멤버 함수를 호출 한 결과는 정의되지 않습니다. 여러분이 할 수있는 유일한 일은 그것이 나온 클래스로 다시 캐스팅하는 것입니다. 표준에서 실제 컴파일러와 거의 유사하지 않은 영역이기 때문에이 기사 뒷부분에서이 문제에 대해 자세히 설명하겠습니다.
왜이 작업을 원하십니까? 따라서 동일한 컨테이너에 여러 클래스의 객체에 대한 멤버 함수 포인터를 저장하고 런타임에 호출 할 함수 포인터를 선택할 수 있습니다. (코드는 멤버 함수 포인터가 객체되는 호출 합법적 런타임 추적 것으로 가정한다.) 주조 멤버 함수 포인터를 저장하는 것은 실제로 구현되는 컴파일러에 상기 링크 된 문서 당
class TypeEraser; // Not a base of anything.
typedef void (TypeEraser::*erased_fptr)();
map<string, erased_fptr> functions;
// Casting & storage as if member function of unrelated class is in the standard
functions["MyFunc"] = reinterpret_cast<erased_fptr>(&MyClass::MyFunc);
TypeEraser* my_obj = (TypeEraser*)(void*)new MyClass;
erased_fpr my_func = functions["MyFunc"];
// !!! But calling it is undefined behavior according to standard !!!
my_obj->*my_func();
, 호출도 예상대로 작동합니다. 그러나 (다시 말하지만) 모든 컴파일러가 실제로 캐스팅과 저장을 구현하는 것은 아닙니다. 즉, 캐스팅 및 저장은 표준이지만 이식 가능하지는 않지만 멤버 함수 포인터를 호출하는 것은 표준이 아니지만 이전 함수가 작동하는 경우 작동합니다. 둘 다 표준적이고 이식성이 있다면 더 좋을 것입니다.
그렇습니다. 람다, 기본 클래스를 가진 펑터 등등이 목표를 달성하기위한 몇 가지 다른 방법이 있습니다. 이러한 모든 대안이 짧아지는 곳은 모두 컴파일러가 추가 클래스를 방출하도록합니다. 개체 파일의 멤버. 개인적으로 문제를 고려하지 않을 수도 있지만 많은 수의 멤버 함수 포인터가 저장되는 유스 케이스에서는 단순히 멤버 함수의 주소를 취하는 것보다 훨씬 많은 객체 파일과 컴파일 시간을 필요로합니다.
왜 * * 정의되지 않은 동작이되어서는 안되는 이유는 없습니다. 기본적으로'void (*) (void *)'포인터에'void (*) (MyClass *)'함수 포인터를 캐스팅 한 후'MyClass * '로 후자를 호출하는 것과 다르지 않습니다. 저도 UB입니다. 대부분의 컴파일러에서이 문제를 해결할 수 있다는 사실은 부적절합니다. 의미 론적으로, 그것은 말도 안돼. –
"대부분의 컴파일러에서, 이것은 어쨌든 작동합니다." 아니, 그렇지 않아. 당신이 기대하는 일을하는 사소한 경우가있을 수 있지만, 다른 클래스의 객체를 가리키는'void * '를 가진 임의의 클래스의 멤버 함수를 호출하는 것은 작동하지 않아도되고 실제로, 일반적으로 작동합니다.특히, 가장 중요한 고객을 위해 프로그램을 시연 할 때 항상 실패합니다. –
나를 downvoting하기 전에 링크 된 기사를 읽어보십시오. 이 기사의 저자는 실증적 인 조사와 표준의 다른 부분에서 멤버 함수를 호출하는 기능이 정의 된 동작이어야한다는 논리적 인 주장을 제시했습니다. – Dennis