질문의 "숨겨진 기능"과 관련하여 C ++를 좋아하지 않습니까? 나는 그것을 밖으로 버릴 것이라고 생각했다. C ++의 숨겨진 기능은 무엇입니까?
질문의 "숨겨진 기능"과 관련하여 C ++를 좋아하지 않습니까? 나는 그것을 밖으로 버릴 것이라고 생각했다. C ++의 숨겨진 기능은 무엇입니까?
답변:
대부분의 C ++ 프로그래머는 삼항 연산자에 익숙합니다.
x = (y < 0) ? 10 : 20;
그러나 그들은 그것이 lvalue로 사용될 수 있다는 것을 깨닫지 못합니다.
(a == 0 ? a : b) = 1;
약어입니다
if (a == 0)
a = 1;
else
b = 1;
주의해서 사용하십시오 :-)
(value ? function1 : function2)()
.
function1
있고 function2
암시 적으로 함수 포인터로 변환되고 결과가 암시 적으로 다시 변환 되면 모든 곳에서 작동 합니다.
오류없이 URI를 C ++ 소스에 넣을 수 있습니다. 예를 들면 :
void foo() {
http://stackoverflow.com/
int bar = 4;
...
}
goto
C ++에 있는와 함께 사용하기위한 것입니다 . 두 개의 슬래시 뒤에 오는 것은 주석입니다. 따라서,와 http://stackoverflow.com
, http
라벨은 (당신이 이론적으로 쓸 수있다 goto http;
), 그리고 //stackoverflow.com
그냥 끝 (end-of-line) 주석입니다. 둘 다 합법적 인 C ++이므로 구문이 컴파일됩니다. 물론 막연하게 유용한 것은 없습니다.
goto http;
실제로 URL을 따르지 않습니다. :(
대부분의 게시물에 동의합니다. C ++는 다중 패러다임 언어이므로 찾을 수있는 "숨겨진"기능 (모든 대가를 치르는 "정의되지 않은 동작"제외)은 시설의 현명한 사용입니다.
이러한 기능의 대부분은 언어의 내장 기능이 아니라 라이브러리 기반 기능입니다.
가장 중요한 것은 RAII 인데, C 세계에서 온 C ++ 개발자들이 수년 동안 무시하는 경우가 많습니다. 연산자 오버로딩 은 종종 배열과 같은 동작 (아래 첨자 연산자), 포인터와 같은 작업 (스마트 포인터) 및 내장형 작업 (행렬 곱하기)을 모두 가능하게하는 오해 된 기능입니다.
예외 의 사용 은 종종 어렵지만 일부 작업을 수행하면 예외 안전 사양 (실패하지 않는 코드 또는 성공할 커밋과 유사한 기능을 포함하는 코드 포함)을 통해 정말 강력한 코드를 생성 할 수 있습니다. 원래 상태).
C ++의 가장 유명한 "숨겨진"기능은 템플릿 메타 프로그래밍 입니다. 이는 런타임 대신 컴파일 시간에 프로그램을 부분적으로 (또는 전체적으로) 실행할 수있게 해주기 때문입니다. 그러나 이것은 어렵고 템플릿을 시도하기 전에 확실한 이해가 있어야합니다.
다른 사람들은 C ++의 조상, 즉 C 외부에서 "프로그래밍 방법"을 생성하기 위해 다중 패러다임을 사용합니다.
functors 를 사용 하면 추가 유형 안전성 및 상태 저장 기능 을 사용하여 함수를 시뮬레이션 할 수 있습니다. 명령 패턴을 사용하여 코드 실행을 지연 할 수 있습니다. 대부분의 다른 디자인 패턴 은 "공식 C ++ 패러다임"목록에 포함되지 않아야하는 대체 코딩 스타일을 생성하기 위해 C ++로 쉽고 효율적으로 구현할 수 있습니다.
템플릿 을 사용 하면 처음에 생각했던 유형을 포함하여 대부분의 유형에서 작동하는 코드를 생성 할 수 있습니다. 유형 안전성도 높일 수 있습니다 (자동화 된 typesafe malloc / realloc / free처럼). C ++ 객체 기능은 정말 강력하지만 (따라서 부주의하게 사용하면 위험합니다) 동적 다형성 도 C ++에서 정적 버전 인 CRTP를 갖습니다 .
나는 Scott Meyers의 대부분의 " Effective C ++ "유형의 책이나 Herb Sutter의 " Exceptional C ++ "유형의 책은 읽기 쉽고 C ++의 알려진 기능과 덜 알려진 기능에 대한 정보의 귀중함을 발견했습니다.
내가 선호하는 것 중 하나는 자바 프로그래머의 머리를 공포에서 벗어나게 만드는 것입니다. C ++에서 객체에 기능을 추가하는 가장 객체 지향적 인 방법은 멤버가 아닌 멤버가 아닌 친구 함수를 사용하는 것입니다. 함수 (즉, 클래스 메서드)는 다음과 같은 이유 때문입니다.
C ++에서 클래스의 인터페이스는 동일한 네임 스페이스의 멤버 함수이자 비 멤버 함수입니다.
친구가 아닌 비회원 함수는 내부 클래스에 대한 권한있는 액세스 권한이 없습니다. 따라서 멤버가 아닌 친구보다 멤버 함수를 사용하면 클래스의 캡슐화가 약화됩니다.
이것은 경험 많은 개발자조차도 놀라지 않습니다.
(출처 : Herb Sutter의 온라인 Guru of the Week # 84 : http://www.gotw.ca/gotw/084.htm )
학교에서 내내 들어 본 적이 없었기 때문에 다소 숨겨져 있다고 생각하는 언어 기능 중 하나는 네임 스페이스 별칭입니다. 부스트 문서에서 예제를 볼 때까지 내 관심을 끌지 못했습니다. 물론, 이제는 표준 C ++ 참조에서 찾을 수 있습니다.
namespace fs = boost::filesystem;
fs::path myPath( strPath, fs::native );
using
.
for
루프 의 init 부분에서 변수를 선언 할 수있을 뿐만 아니라 클래스와 함수도 선언 할 수 있습니다 .
for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
...
}
이는 유형이 다른 여러 변수를 허용합니다.
배열 연산자는 연관 적입니다.
A [8]은 * (A + 8)의 동의어입니다. 덧셈은 연관성이므로 * (8 + A)로 다시 쓸 수 있습니다. 이것은 ....... 8 [A]의 동의어입니다.
유용하다고 말하지 않았는데 ... :-)
A
은 전혀 중요하지 않습니다. 예를 들어, A
을했다 char*
, 코드는 여전히 유효 할 것이다.
잘 알려지지 않은 한 가지는 공용체도 템플릿이 될 수 있다는 것입니다.
template<typename From, typename To>
union union_cast {
From from;
To to;
union_cast(From from)
:from(from) { }
To getTo() const { return to; }
};
그리고 생성자와 멤버 함수도 가질 수 있습니다. 상속 (가상 함수 포함)과 관련된 것은 없습니다.
From
하고 To
그에 따라 설정하고 사용된다. 이러한 공용체는 정의 된 동작과 함께 사용할 수 있습니다 ( To
부호없는 문자의 배열 또는 초기 시퀀스를 공유하는 구조체 From
). 정의되지 않은 방식으로 사용하더라도 낮은 수준의 작업에는 여전히 유용 할 수 있습니다. 어쨌든, 이것은 유니온 템플릿의 한 예일뿐입니다. 템플릿 화 된 유니온에 다른 용도가있을 수 있습니다.
C ++는 표준이며 숨겨진 기능이 없어야합니다.
C ++는 다중 패러다임 언어이므로 숨겨진 기능에 마지막 돈을 걸 수 있습니다. 여러 가지 중 한 가지 예 : 템플릿 메타 프로그래밍 . 표준위원회의 어느 누구도 컴파일 타임에 실행되는 Turing-complete 하위 언어를 의도하지 않았습니다.
C에서 작동하지 않는 또 다른 숨겨진 기능은 단항 +
연산자 의 기능입니다 . 모든 종류의 것을 홍보하고 부패시키는 데 사용할 수 있습니다.
+AnEnumeratorValue
그리고 이전에 열거 유형이 있던 열거 자 값은 이제 해당 값에 맞는 완벽한 정수 유형을 갖습니다. 수동으로 그 유형을 거의 알 수 없습니다! 예를 들어 열거 형에 대해 오버로드 된 연산자를 구현하려는 경우에 필요합니다.
out of class 정의없이 in-class static initializer를 사용하는 클래스를 사용해야하지만 때때로 링크에 실패합니까? 연산자는 유형에 대한 assumptin 또는 종속성을 만들지 않고 임시를 만드는 데 도움이 될 수 있습니다.
struct Foo {
static int const value = 42;
};
// This does something interesting...
template<typename T>
void f(T const&);
int main() {
// fails to link - tries to get the address of "Foo::value"!
f(Foo::value);
// works - pass a temporary value
f(+Foo::value);
}
함수에 두 개의 포인터를 전달하고 싶지만 작동하지 않습니까? 운영자가 도움을 줄 수 있습니다.
// This does something interesting...
template<typename T>
void f(T const& a, T const& b);
int main() {
int a[2];
int b[3];
f(a, b); // won't work! different values for "T"!
f(+a, +b); // works! T is "int*" both time
}
const 참조에 바인딩 된 임시의 수명은 거의 아는 사람이 거의 없습니다. 또는 적어도 대부분의 사람들이 알지 못하는 C ++ 지식 중 제가 가장 좋아하는 부분입니다.
const MyClass& x = MyClass(); // temporary exists as long as x is in scope
자주 사용되지 않는 멋진 기능은 함수 전체의 try-catch 블록입니다.
int Function()
try
{
// do something here
return 42;
}
catch(...)
{
return -1;
}
주요 용도는 예외를 다른 예외 클래스로 변환하고 다시 던지거나 예외와 반환 기반 오류 코드 처리간에 변환하는 것입니다.
return
이 Function Try의 catch 블록에서 할 수 있다고 생각하지 않습니다 .
identity
/ id
메타 함수 에 대해 많은 사람들이 알고 있지만 템플릿이 아닌 경우에 대한 좋은 사용 사례가 있습니다. 선언 작성 용이 :
// void (*f)(); // same
id<void()>::type *f;
// void (*f(void(*p)()))(int); // same
id<void(int)>::type *f(id<void()>::type *p);
// int (*p)[2] = new int[10][2]; // same
id<int[2]>::type *p = new int[10][2];
// void (C::*p)(int) = 0; // same
id<void(int)>::type C::*p = 0;
C ++ 선언을 해독하는 데 큰 도움이됩니다!
// boost::identity is pretty much the same
template<typename T>
struct id { typedef T type; };
template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;
.-> pointer<function<void,int>> f(pointer<function<void,void>>);
또는 pointer<void(int)> f(pointer<void()>);
또는function<pointer<function<void,int>>,pointer<function<void,void>>> f;
매우 숨겨진 기능은 if 조건 내에서 변수를 정의 할 수 있으며 해당 범위는 if 및 else 블록에만 적용된다는 것입니다.
if(int * p = getPointer()) {
// do something
}
일부 매크로는이를 사용하여 예를 들어 다음과 같이 "잠긴"범위를 제공합니다.
struct MutexLocker {
MutexLocker(Mutex&);
~MutexLocker();
operator bool() const { return false; }
private:
Mutex &m;
};
#define locked(mutex) if(MutexLocker const& lock = MutexLocker(mutex)) {} else
void someCriticalPath() {
locked(myLocker) { /* ... */ }
}
또한 BOOST_FOREACH는 내부적으로 그것을 사용합니다. 이를 완료하려면 if뿐만 아니라 스위치에서도 가능합니다.
switch(int value = getIt()) {
// ...
}
그리고 while 루프에서 :
while(SomeThing t = getSomeThing()) {
// ...
}
(또한 for 조건). 그러나 이것이 모두 유용한 지 확실하지 않습니다. :)
if((a = f()) == b) ...
하지만이 대답은 실제로 조건에서 변수를 선언합니다.
for(...; int i = foo(); ) ...;
한 본문을 통과하여 i
매번 다시 초기화합니다. 당신이 보여주는 루프는 단순히 변수 선언을 보여 주지만 동시에 조건으로 작용하는 변수 선언은 아닙니다. :)
때로는 쉼표 연산자를 유효하게 사용하지만 사용자 정의 쉼표 연산자가 방해가되지 않도록하고 싶을 때가 있습니다. 동작. 이것이 void()
게임에 등장하는 곳 입니다.
for(T i, j; can_continue(i, j); ++i, void(), ++j)
do_code(i, j);
조건과 코드에 대한 자리 표시자를 무시하십시오. 중요한 것은 void()
컴파일러가 내장 쉼표 연산자를 사용하도록 만드는입니다. 이것은 때때로 트레이 트 클래스를 구현할 때도 유용 할 수 있습니다.
생성자의 배열 초기화. 예를 들어 다음과 int
같은 배열이있는 클래스에서 :
class clName
{
clName();
int a[10];
};
생성자에서 배열의 모든 요소를 기본값 (여기서는 배열의 모든 요소를 0으로)으로 초기화 할 수 있습니다.
clName::clName() : a()
{
}
오, 대신 애완 동물 증오 목록을 만들 수 있습니다.
긍정적 인 측면에서는
정의되지 않은 동작없이 예상되는 의미를 사용하여 모든 클래스의 보호 된 데이터 및 함수 멤버에 액세스 할 수 있습니다. 방법을 보려면 계속 읽으십시오. 이에 대한 결함 보고서 도 읽어보십시오 .
일반적으로 C ++는 해당 클래스가 기본 클래스 인 경우에도 클래스 개체의 비 정적 보호 멤버에 액세스하는 것을 금지합니다.
struct A {
protected:
int a;
};
struct B : A {
// error: can't access protected member
static int get(A &x) { return x.a; }
};
struct C : A { };
그것은 금지되어 있습니다. 당신과 컴파일러는 참조가 실제로 무엇을 가리키는 지 모릅니다. C
클래스 B
가 데이터에 대한 단서가없는 경우 객체 일 수 있습니다 . 이러한 액세스는가 x
파생 클래스에 대한 참조이거나 파생 클래스에 대한 참조 인 경우에만 부여됩니다 . 그리고 다음과 같이 멤버를 읽는 "throw-away"클래스를 구성함으로써 임의의 코드 조각이 보호 된 멤버를 읽을 수 있습니다 std::stack
.
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
static std::deque<int> &get(std::stack<int> &s) {
// error: stack<int>::c is protected
return s.c;
}
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = pillager::get(s);
}
분명히, 이것은 너무 많은 피해를 입힐 것입니다. 하지만 이제 멤버 포인터를 사용하면 이러한 보호를 우회 할 수 있습니다! 요점은 멤버 포인터의 유형 이 주소를 가져올 때 지정한 클래스가 아니라 실제로 해당 멤버를 포함하는 클래스에 바인딩 된다는 것입니다. 이를 통해 확인을 우회 할 수 있습니다.
struct A {
protected:
int a;
};
struct B : A {
// valid: *can* access protected member
static int get(A &x) { return x.*(&B::a); }
};
struct C : A { };
물론 std::stack
예제 에서도 작동합니다 .
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
static std::deque<int> &get(std::stack<int> &s) {
return s.*(pillager::c);
}
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = pillager::get(s);
}
멤버 이름을 공개하고 기본 클래스의 멤버를 참조하는 파생 클래스에서 using 선언을 사용하면 훨씬 더 쉬워집니다.
void f(std::stack<int> &s) {
// now, let's decide to mess with that stack!
struct pillager : std::stack<int> {
using std::stack<int>::c;
};
// haha, now let's inspect the stack's middle elements!
std::deque<int> &d = s.*(&pillager::c);
}
또 다른 숨겨진 기능은 함수 포인터 또는 참조로 변환 할 수있는 클래스 객체를 호출 할 수 있다는 것입니다. 그 결과에 대해 과부하 해결이 수행되고 인수가 완벽하게 전달됩니다.
template<typename Func1, typename Func2>
class callable {
Func1 *m_f1;
Func2 *m_f2;
public:
callable(Func1 *f1, Func2 *f2):m_f1(f1), m_f2(f2) { }
operator Func1*() { return m_f1; }
operator Func2*() { return m_f2; }
};
void foo(int i) { std::cout << "foo: " << i << std::endl; }
void bar(long il) { std::cout << "bar: " << il << std::endl; }
int main() {
callable<void(int), void(long)> c(foo, bar);
c(42); // calls foo
c(42L); // calls bar
}
이를 "대리 호출 기능"이라고합니다.
숨겨진 기능 :
함수가 예외 사양에 나열되지 않은 예외를 throw하지만 함수 std::bad_exception
에 예외 사양이있는 경우 예외가로 변환되어 std::bad_exception
자동으로 throw됩니다. 그렇게하면 최소한 a bad_exception
가 던져 졌다는 것을 알 수 있습니다. 여기에서 자세한 내용을 읽어보십시오 .
기능 시도 블록
클래스 템플릿에서 typedef를 명확하게하는 템플릿 키워드. 멤버 템플릿 특수화의 이름은 후에 나타나는 경우 .
, ->
또는 ::
운영자, 그 이름은 명시 적으로 규정 템플릿 매개 변수, 키워드 템플릿 접두사 멤버 템플릿 이름이 있습니다. 여기에서 자세한 내용을 읽어보십시오 .
함수 매개 변수 기본값은 런타임에 변경할 수 있습니다. 여기에서 자세한 내용을 읽어보십시오 .
A[i]
잘 작동 i[A]
클래스의 임시 인스턴스를 수정할 수 있습니다! 상수가 아닌 멤버 함수는 임시 개체에서 호출 할 수 있습니다. 예를 들면 :
struct Bar {
void modify() {}
}
int main (void) {
Bar().modify(); /* non-const function invoked on a temporary. */
}
여기에서 자세한 내용을 읽어보십시오 .
:
삼항 ( ?:
) 연산자 표현식 의 앞뒤에 두 개의 다른 유형이있는 경우 표현식의 결과 유형은 둘 중 가장 일반적인 유형입니다. 예를 들면 :
void foo (int) {}
void foo (double) {}
struct X {
X (double d = 0.0) {}
};
void foo (X) {}
int main(void) {
int i = 1;
foo(i ? 0 : 0.0); // calls foo(double)
X x;
foo(i ? 0.0 : x); // calls foo(X)
}
map::operator[]
키가없는 경우 항목을 만들고 기본 구성 항목 값에 대한 참조를 반환합니다. 따라서 다음과 같이 작성할 수 있습니다.
map<int, string> m;
string& s = m[42]; // no need for map::find()
if (s.empty()) { // assuming we never store empty values in m
s.assign(...);
}
cout << s;
나는 얼마나 많은 C ++ 프로그래머가 이것을 모른다는 것에 놀랐다.
.find()
.
const map::operator[]
오류 메시지를 생성합니다"
이름없는 네임 스페이스에 함수 나 변수를 넣으면를 사용 static
하여 파일 범위로 제한 할 수 없습니다.
static
글로벌 범위에서 어떤 식 으로든 사용되지 않습니다. (참고 : C ++ 03 §D.2)
static
use는 클래스 유형 또는 함수 내에서만 사용해야합니다.
클래스 템플릿에서 일반적인 친구 함수를 정의하려면 특별한주의가 필요합니다.
template <typename T>
class Creator {
friend void appear() { // a new function ::appear(), but it doesn't
… // exist until Creator is instantiated
}
};
Creator<void> miracle; // ::appear() is created at this point
Creator<double> oops; // ERROR: ::appear() is created a second time!
이 예에서 두 개의 서로 다른 인스턴스화는 두 개의 동일한 정의를 만듭니다. 즉, ODR을 직접 위반합니다.
따라서 클래스 템플릿의 템플릿 매개 변수가 해당 템플릿에 정의 된 친구 함수의 유형에 나타나는지 확인해야합니다 (특정 파일에서 클래스 템플릿의 인스턴스화를 두 번 이상 방지하려는 경우가 아니라면). 이것을 이전 예제의 변형에 적용 해 보겠습니다.
template <typename T>
class Creator {
friend void feed(Creator<T>*){ // every T generates a different
… // function ::feed()
}
};
Creator<void> one; // generates ::feed(Creator<void>*)
Creator<double> two; // generates ::feed(Creator<double>*)
면책 조항 : C ++ 템플릿 : 전체 가이드 / 섹션 8.4 에서이 섹션을 붙여 넣었습니다 .
거의 알려지지 않았지만 다음 코드는 괜찮습니다.
void f() { }
void g() { return f(); }
다음과 같이 이상하게 보이는 하나
void f() { return (void)"i'm discarded"; }
이에 대해 알면 일부 영역에서 이점을 얻을 수 있습니다. 한 가지 예 : void
함수는 값을 반환 할 수 없지만 무효가 아닌 것으로 인스턴스화 될 수 있기 때문에 아무것도 반환하지 않을 수도 있습니다. 에 대한 오류가 발생하는 지역 변수에 값을 저장하는 대신 void
값을 직접 반환합니다.
template<typename T>
struct sample {
// assume f<T> may return void
T dosomething() { return f<T>(); }
// better than T t = f<T>(); /* ... */ return t; !
};
파일을 문자열 벡터로 읽어들입니다.
vector<string> V;
copy(istream_iterator<string>(cin), istream_iterator<string>(),
back_inserter(V));
vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());
- 두 번째 PARAM 후 괄호가 누락
비트 필드를 템플릿 할 수 있습니다.
template <size_t X, size_t Y>
struct bitfield
{
char left : X;
char right : Y;
};
나는 아직 이것에 대한 어떤 목적도 생각해 내지 못했지만, 정말 놀랐습니다.
모든 프로그래밍 언어 중 가장 흥미로운 문법 중 하나입니다.
이 중 세 가지는 함께 속하고 두 가지는 완전히 다른 것입니다 ...
SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));
세 번째와 다섯 번째를 제외한 모든 SomeType
객체는 스택에 객체를 정의 하고 초기화합니다 ( u
처음 두 경우에는 기본 생성자가 있고 네 번째 경우에는 기본 생성자가 있습니다. 세 번째는 매개 변수를받지 않고를 반환하는 함수를 선언합니다 SomeType
. 다섯 번째는 비슷하게 선언합니다. SomeType
라는 유형의 값으로 하나의 매개 변수를 취하는 함수 u
.
전방 선언 제거 :
struct global
{
void main()
{
a = 1;
b();
}
int a;
void b(){}
}
singleton;
? : 연산자를 사용하여 스위치 문 작성 :
string result =
a==0 ? "zero" :
a==1 ? "one" :
a==2 ? "two" :
0;
한 줄로 모든 작업 수행 :
void a();
int b();
float c = (a(),b(),1.0f);
memset없이 구조체 제로화 :
FStruct s = {0};
각도 및 시간 값 정규화 / 래핑 :
int angle = (short)((+180+30)*65536/360) * 360/65536; //==-150
참조 할당 :
struct ref
{
int& r;
ref(int& r):r(r){}
};
int b;
ref a(b);
int c;
*(int**)&a = &c;
FStruct s = {};
더 짧습니다.
main
않나요? global().main();
싱글 톤에 대해 제안 하고 잊어 버릴 것입니다 ( 임시로 작업 할 수 있으며 수명이 연장됨 )
삼항 조건부 연산자 ?:
는 두 번째 및 세 번째 피연산자가 "동의 할 수있는"유형 (비공식적으로 말함)을 가져야합니다. 그러나이 요구 사항에는 한 가지 예외 (의도 된 말장난)가 있습니다. 두 번째 또는 세 번째 피연산자는 void
다른 피연산자의 유형에 관계없이 throw 식 (type ) 이 될 수 있습니다 .
즉, ?:
연산자를 사용하여 다음과 같이 유효하게 유효한 C ++ 표현식을 작성할 수 있습니다.
i = a > b ? a : throw something();
BTW, throw 표현식이 실제로 는 표현식 (유형의 void
)이고 문이 아니라는 사실 은 C ++ 언어의 또 다른 잘 알려지지 않은 기능입니다. 이것은 무엇보다도 다음 코드가 완벽하게 유효 함을 의미합니다.
void foo()
{
return throw something();
}
이런 식으로 수행하는 데 많은 의미가 없지만 (일부 일반적인 템플릿 코드에서는 유용 할 수 있습니다).
지배 규칙은 유용하지만 거의 알려지지 않았습니다. 기본 클래스 격자를 통한 고유하지 않은 경로에 있더라도 멤버가 가상 기본 클래스에 속하면 부분적으로 숨겨진 멤버에 대한 이름 조회가 고유하다고 말합니다.
struct A { void f() { } };
struct B : virtual A { void f() { cout << "B!"; } };
struct C : virtual A { };
// name-lookup sees B::f and A::f, but B::f dominates over A::f !
struct D : B, C { void g() { f(); } };
나는 이것을 사용 하여 지배 규칙을 통해 가장 엄격한 정렬을 자동으로 파악하는 정렬 지원 을 구현했습니다 .
이것은 가상 함수뿐만 아니라 typedef 이름, 정적 / 비가 상 멤버 및 기타 모든 것에 적용됩니다. 메타 프로그램에서 덮어 쓸 수있는 특성을 구현하는 데 사용되는 것을 보았습니다.
struct C
당신의 예에 포함 된 특별한 이유는 ...? 건배.