C ++의 숨겨진 기능? [닫은]


114

질문의 "숨겨진 기능"과 관련하여 C ++를 좋아하지 않습니까? 나는 그것을 밖으로 버릴 것이라고 생각했다. C ++의 숨겨진 기능은 무엇입니까?


@Devtron-기능으로 판매되는 멋진 버그 (예 : 예기치 않은 동작)를 보았습니다. 사실, 게임 산업은 실제로 이런 일이 일어나도록 노력하고 있으며이를 "긴급 게임 플레이"라고 부릅니다 (또한 Psi-Ops의 "TK Surfing"도 확인하세요. 순전히 버그 였지만 그대로 두었습니다. 게임 IMHO의 최고의 기능)
— Grant Peters

5
@Laith J : 786 페이지의 ISO C ++ 표준을 처음부터 끝까지 읽은 사람은 많지 않습니다.하지만 여러분은 이미 읽었을 것입니다.
— j_random_hacker

2
@Laith, @j_random : "나는 그것을 인식 어떻게, 프로그래머의 농담을하고, 무엇을 적절한 응답 무엇인가"에 내 질문을 참조하십시오 stackoverflow.com/questions/1/you-have-been-link-rolled을 .

답변:


308

대부분의 C ++ 프로그래머는 삼항 연산자에 익숙합니다.

x = (y < 0) ? 10 : 20;

그러나 그들은 그것이 lvalue로 사용될 수 있다는 것을 깨닫지 못합니다.

(a == 0 ? a : b) = 1;

약어입니다

if (a == 0)
    a = 1;
else
    b = 1;

주의해서 사용하십시오 :-)


11
매우 흥미로운. 그래도 읽을 수없는 코드를 만드는 것을 볼 수 있습니다.
— Jason Baker

112
Yikes. (a == 0? a : b) = (y <0? 10:20);
— Jasper Bekkers

52
(b? trueCount : falseCount) ++
— Pavel Radzivilovsky

12
Dunno가 GCC에 특화된 경우, 이것도 효과가 있다는 사실에 놀랐습니다 (value ? function1 : function2)().
— Chris Burt-Brown

3
@Chris Burt-Brown : 아니요, 동일한 유형 (즉, 기본 인수가 없음)이 function1있고 function2암시 적으로 함수 포인터로 변환되고 결과가 암시 적으로 다시 변환 되면 모든 곳에서 작동 합니다.
— MSalters

238

오류없이 URI를 C ++ 소스에 넣을 수 있습니다. 예를 들면 :

void foo() {
    http://stackoverflow.com/
    int bar = 4;

    ...
}

41
그러나 기능 당 하나만 의심됩니다. :)
— Constantin

51
@jpoh : http 다음에 콜론이 오면 나중에 goto 문에서 사용하는 "라벨"이됩니다. 위의 예에서 어떤 goto 문에도 사용되지 않았기 때문에 컴파일러에서 경고를받습니다.
— utku_karatas

9
프로토콜이 다른 한 둘 이상을 추가 할 수 있습니다! ftp.microsoft.com gopher : //aerv.nl/1 등 ...
— Daniel Earwicker

4
@Pavel : 식별자 뒤에 콜론이 오는 레이블은 gotoC ++에 있는와 함께 사용하기위한 것입니다 . 두 개의 슬래시 뒤에 오는 것은 주석입니다. 따라서,와 http://stackoverflow.com, http라벨은 (당신이 이론적으로 쓸 수있다 goto http;), 그리고 //stackoverflow.com그냥 끝 (end-of-line) 주석입니다. 둘 다 합법적 인 C ++이므로 구문이 컴파일됩니다. 물론 막연하게 유용한 것은 없습니다.
— David Thornley

8
불행히도 goto http;실제로 URL을 따르지 않습니다. :(
— Yakov Galka 2011-08-02

140

포인터 산술.

C ++ 프로그래머는 발생할 수있는 버그 때문에 포인터를 피하는 것을 선호합니다.

내가 본 것 중 가장 멋진 C ++? 아날로그 리터럴.


11
버그 때문에 포인터를 피합니까? 포인터는 기본적으로 동적 C ++ 코딩의 모든 것입니다!
— Nick Bedford

1
아날로그 리터럴은 난독 화 된 C ++ 컨테스트 항목, 특히 ASCII 아트 유형에 적합합니다.
— Synetech

119

대부분의 게시물에 동의합니다. 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 )


+1 매우 철저한 답변. 명백한 이유 때문에 불완전합니다 (그렇지 않으면 더 이상 "숨겨진 기능"이 없을 것입니다!). : p 대답 끝의 첫 번째 지점에서 클래스 인터페이스의 멤버를 언급했습니다. ".. 멤버 함수이자 친구 비 멤버 함수입니다"라는 뜻입니까?
— wilhelmtell


1에 대해 언급하는 것은 koenig 조회 여야합니다. 맞죠?
— Özgür

1
@wilhelmtell : 안돼 안돼 안돼 ... :-p ... "멤버 기능과 비-친구 비회원 기능"을 의미합니다 .... Koenig의 Lookup은 이러한 기능이 다른 기능보다 빨리 고려되도록 할 것입니다. " 외부 "기능을 사용하여 기호 검색
— paercebal

7
아주 소수의 사람들이 깨닫는 멋진 게시물, 특히 마지막 부분에 대한 +1. 아마도 Boost 라이브러리를 "숨겨진 기능"으로 추가 할 것입니다. 나는 그것을 C ++이 가지고 있어야하는 표준 라이브러리라고 생각한다. ;)
— jalf

118

학교에서 내내 들어 본 적이 없었기 때문에 다소 숨겨져 있다고 생각하는 언어 기능 중 하나는 네임 스페이스 별칭입니다. 부스트 문서에서 예제를 볼 때까지 내 관심을 끌지 못했습니다. 물론, 이제는 표준 C ++ 참조에서 찾을 수 있습니다.

namespace fs = boost::filesystem;

fs::path myPath( strPath, fs::native );

1
사용하지 않으려는 경우 유용하다고 생각합니다 using.
— Siqi Lin 2010-08-20

4
또한 스레드 안전과 비 스레드 안전 또는 버전 1 대 2를 선택하든 구현간에 전환하는 방법으로도 유용합니다.
— Tony Delroy 2011 년

3
네임 스페이스 계층이 큰 대규모 프로젝트에서 작업하고 헤더가 네임 스페이스 오염을 일으키지 않도록하는 경우 (그리고 변수 선언을 사람이 읽을 수 있도록하려는 경우) 특히 유용합니다.
— Brandon Bohrer 2011 년

102

for루프 의 init 부분에서 변수를 선언 할 수있을 뿐만 아니라 클래스와 함수도 선언 할 수 있습니다 .

for(struct { int a; float b; } loop = { 1, 2 }; ...; ...) {
    ...
}

이는 유형이 다른 여러 변수를 허용합니다.


31
당신이 할 수 있다는 것을 알게되어 기쁩니다.하지만 개인적으로 저는 그런 일을하지 않으려 고합니다. 대부분 읽기가 어렵 기 때문입니다.
— Zoomulator

2
실제로이 컨텍스트에서 작동하는 것은 쌍을 사용하는 것입니다. for (std :: pair <int, float> loop = std :: make_pair (1,2); loop.first> 0; loop.second + = 1)
— Valentin Heinitz 2010

2
@Valentin 그럼 숨겨진 기능을 반대하는 대신 VS2008에 대한 버그 보고서를 작성하는 것이 좋습니다. 그것은 분명히 컴파일러의 잘못입니다.
— Johannes Schaub-litb

2
흠, msvc10에서도 작동하지 않습니다. 얼마나 슬픈 지 :(
— avakar

2
@avakar 사실, gcc는 v4.6에서도 거부하는 버그를 도입했습니다. :) gcc.gnu.org/bugzilla/show_bug.cgi?id=46791
— Johannes Schaub-litb

77

배열 연산자는 연관 적입니다.

A [8]은 * (A + 8)의 동의어입니다. 덧셈은 연관성이므로 * (8 + A)로 다시 쓸 수 있습니다. 이것은 ....... 8 [A]의 동의어입니다.

유용하다고 말하지 않았는데 ... :-)


15
사실,이 트릭을 사용할 때는 어떤 유형을 사용하는지에주의를 기울여야합니다. A [8]은 실제로 8 번째 A이고 8 [A]는 주소 8에서 시작하는 A 번째 정수입니다. A가 바이트이면 버그가 있습니다.
— Vincent Robert

38
"연관 적"이라고 말하는 곳에 "교환 적"을 의미합니까?
— DarenW

28
빈센트, 틀렸어. 유형 A은 전혀 중요하지 않습니다. 예를 들어, A을했다 char*, 코드는 여전히 유효 할 것이다.
— Konrad Rudolph

11
A는 반드시 포인터 여야하며 operator []를 오버로딩하는 클래스가 아니어야합니다.
— David Rodríguez-dribeas

15
Vincent, 여기에는 하나의 정수 유형과 하나의 포인터 유형이 있어야하며 C도 C ++도 어느 것이 먼저 가는지 신경 쓰지 않습니다.
— David Thornley

73

잘 알려지지 않은 한 가지는 공용체도 템플릿이 될 수 있다는 것입니다.

template<typename From, typename To>
union union_cast {
    From from;
    To   to;

    union_cast(From from)
        :from(from) { }

    To getTo() const { return to; }
};

그리고 생성자와 멤버 함수도 가질 수 있습니다. 상속 (가상 함수 포함)과 관련된 것은 없습니다.


흥미 롭군요! 그렇다면 모든 구성원을 초기화해야합니까? 마지막 멤버가 이전 멤버 "위에"초기화된다는 것을 의미하는 일반적인 구조체 순서를 따르는가?
— j_random_hacker

j_random_hacker 오, 맞습니다. 좋은 캐치. 나는 그것이 구조체가 될 것이라고 썼다. 내가 그것을 고칠 것이다 기다립니다
— 요하네스 SCHAUB을 - litb

이것은 정의되지 않은 동작을 호출하지 않습니까?
— Greg Bacon

7
경우 @gbacon, 그래 그것은 정의되지 않은 동작 된 invoke 수행 From하고 To그에 따라 설정하고 사용된다. 이러한 공용체는 정의 된 동작과 함께 사용할 수 있습니다 ( To부호없는 문자의 배열 또는 초기 시퀀스를 공유하는 구조체 From). 정의되지 않은 방식으로 사용하더라도 낮은 수준의 작업에는 여전히 유용 할 수 있습니다. 어쨌든, 이것은 유니온 템플릿의 한 예일뿐입니다. 템플릿 화 된 유니온에 다른 용도가있을 수 있습니다.
— Johannes Schaub-litb

3
생성자에주의하십시오. 첫 번째 요소 만 생성해야하며 C ++ 0x에서만 허용됩니다. 현재 표준에서는 사소하게 구성 가능한 유형을 고수해야합니다. 그리고 소멸자는 없습니다.
— Potatoswatter

72

C ++는 표준이며 숨겨진 기능이 없어야합니다.

C ++는 다중 패러다임 언어이므로 숨겨진 기능에 마지막 돈을 걸 수 있습니다. 여러 가지 중 한 가지 예 : 템플릿 메타 프로그래밍 . 표준위원회의 어느 누구도 컴파일 타임에 실행되는 Turing-complete 하위 언어를 의도하지 않았습니다.


65

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
}

61

const 참조에 바인딩 된 임시의 수명은 거의 아는 사람이 거의 없습니다. 또는 적어도 대부분의 사람들이 알지 못하는 C ++ 지식 중 제가 가장 좋아하는 부분입니다.

const MyClass& x = MyClass(); // temporary exists as long as x is in scope

3
자세히 설명해 주시겠습니까? 당신은 그냥 놀리는 것입니다;)
— Joseph Garvin 2009-06-21

8
ScopeGuard ( ddj.com/cpp/184403758 )는이 기능을 활용하는 좋은 예입니다.
— MSN

2
저는 Joseph Garvin과 함께 있습니다. 우리를 깨달으십시오.
— Peter Mortensen

방금 댓글에서했습니다. 게다가 const 참조 매개 변수를 사용하는 것은 자연스러운 결과입니다.
— MSN


52

자주 사용되지 않는 멋진 기능은 함수 전체의 try-catch 블록입니다.

int Function()
try
{
   // do something here
   return 42;
}
catch(...)
{
   return -1;
}

주요 용도는 예외를 다른 예외 클래스로 변환하고 다시 던지거나 예외와 반환 기반 오류 코드 처리간에 변환하는 것입니다.


나는 당신 return이 Function Try의 catch 블록에서 할 수 있다고 생각하지 않습니다 .
— Constantin

위의 컴파일을 시도했지만 경고가 없었습니다. 위의 예가 작동한다고 생각합니다.
— vividos

7
반환은 생성자에게만 금지됩니다. 생성자의 함수 try 블록은 기본 및 멤버를 초기화하는 오류를 포착합니다 (함수 try 블록이 함수 내부에서 시도를하는 것과 다른 작업을 수행하는 유일한 경우). 다시 던지지 않으면 불완전한 개체가 생성됩니다.
— puetzk

예. 이것은 매우 유용합니다. 예외를 포착하고 HRESULTS를 반환하기 위해 BEGIN_COM_METHOD 및 END_COM_METHOD 매크로를 작성하여 COM 인터페이스를 구현하는 클래스에서 예외가 유출되지 않도록했습니다. 잘 작동했습니다.
— Scott Langham

3
@puetzk가 지적했듯이 이것은 기본 클래스의 생성자 또는 데이터 멤버의 생성자와 같은 생성자의 이니셜 라이저 목록 에있는 모든 항목 에서 발생한 예외를 처리하는 유일한 방법 입니다.
— anton.burger 2010-06-23

44

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; };

흥미롭지 만 처음에는 이러한 정의 중 일부를 읽는 데 더 많은 어려움 이있었습니다 . C ++ 선언의 내부 문제를 해결하는 또 다른 방법은 템플릿 유형 별칭을 작성하는 것입니다 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;
— bames53

42

매우 숨겨진 기능은 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 조건). 그러나 이것이 모두 유용한 지 확실하지 않습니다. :)


산뜻한! 나는 당신이 그렇게 할 수 있다는 것을 결코 몰랐습니다 ... 오류 반환 값으로 코드를 작성할 때 약간의 번거 로움을 덜어 줄 것입니다. 이 형식에서! = 0 대신 조건부를 가질 수있는 방법이 있습니까? if ((int r = func ()) <0) 작동하지 않는 것 같습니다 ...
— puetzk

puetzk, 없습니다. 하지만 다행이 :) 좋아
— 요하네스 SCHAUB - litb

4
@Frerich, 이것은 C 코드에서는 전혀 불가능합니다. 나는 당신이 생각하고 있다고 생각 if((a = f()) == b) ...하지만이 대답은 실제로 조건에서 변수를 선언합니다.
— Johannes Schaub-litb

1
@Angry는 변수 선언이 부울 값에 대해 즉시 테스트되기 때문에 매우 다릅니다. for 루프에 대한 매핑도 있습니다. 이것은 true 인 for(...; int i = foo(); ) ...;한 본문을 통과하여 i매번 다시 초기화합니다. 당신이 보여주는 루프는 단순히 변수 선언을 보여 주지만 동시에 조건으로 작용하는 변수 선언은 아닙니다. :)
— Johannes Schaub-litb

5
이 기능의 의도 된 용도가 동적 포인터 캐스트를위한 것이라고 언급하지 않았다는 점을 제외하면 매우 좋습니다.
— mmocny 2011 년

29

쉼표 연산자가 연산자 오버로드를 호출하지 못하도록 방지

때로는 쉼표 연산자를 유효하게 사용하지만 사용자 정의 쉼표 연산자가 방해가되지 않도록하고 싶을 때가 있습니다. 동작. 이것이 void()게임에 등장하는 곳 입니다.

for(T i, j; can_continue(i, j); ++i, void(), ++j)
  do_code(i, j);

조건과 코드에 대한 자리 표시자를 무시하십시오. 중요한 것은 void()컴파일러가 내장 쉼표 연산자를 사용하도록 만드는입니다. 이것은 때때로 트레이 트 클래스를 구현할 때도 유용 할 수 있습니다.


나는 이것을 사용하여 과잉 표현 무시자를 마무리했습니다 . :)
— GManNickG

28

생성자의 배열 초기화. 예를 들어 다음과 int같은 배열이있는 클래스에서 :

class clName
{
  clName();
  int a[10];
};

생성자에서 배열의 모든 요소를 ​​기본값 (여기서는 배열의 모든 요소를 ​​0으로)으로 초기화 할 수 있습니다.

clName::clName() : a()
{
}

6
어디서나 모든 어레이로이 작업을 수행 할 수 있습니다.
— Potatoswatter

@Potatoswatter : 가장 짜증나는 구문 분석으로 인해보기보다 어렵습니다. 나는 그것이 제외 할 수있는 다른 곳을 생각할 수 없다 어쩌면 반환 값
— 오리 음매을

배열의 유형이 클래스 유형이면 필요하지 않습니까?
— Thomas Eding 2012

27

오, 대신 애완 동물 증오 목록을 만들 수 있습니다.

  • 다형성으로 사용하려는 경우 소멸자는 가상이어야합니다.
  • 때때로 멤버는 기본적으로 초기화되지만 때로는 초기화되지 않습니다.
  • 로컬 클래스는 템플릿 매개 변수로 사용할 수 없습니다 (유용성이 떨어짐)
  • 예외 지정자 : 유용 해 보이지만 그렇지 않습니다.
  • 함수 오버로드는 서명이 다른 기본 클래스 함수를 숨 깁니다.
  • 국제화에 대한 유용한 표준화가 없습니다 (포터블 표준 와이드 문자 세트, 누구? C ++ 0x까지 기다려야합니다)

긍정적 인 측면에서는

  • 숨겨진 기능 : 기능 시도 블록. 불행히도 나는 그것을 사용하지 못했습니다. 예, 왜 추가했는지 알지만 생성자에서 다시 던져야 무의미합니다.
  • 컨테이너 수정 후 반복기 유효성에 대한 STL 보장을주의 깊게 살펴보면 약간 더 멋진 루프를 만들 수 있습니다.
  • 부스트-비밀은 아니지만 사용할 가치가 있습니다.
  • 반환 값 최적화 (명백하지는 않지만 표준에서 특별히 허용됨)
  • Functors aka function objects aka operator (). 이것은 STL에서 광범위하게 사용됩니다. 실제로 비밀은 아니지만 연산자 오버로딩 및 템플릿의 멋진 부작용입니다.

16
애완 동물 증오 : 모든 언어가 C 함수를 호출하도록 보장 할 수 있기 때문에 모든 언어가 사용하는 C 앱과 달리 C ++ 앱에 대해 정의 된 ABI가 없으며 아무도 C ++에 대해 동일한 작업을 수행 할 수 없습니다.
— gbjbaanb

8
소멸자는 다형성으로 파괴하려는 경우에만 가상이어야합니다. 이는 첫 번째 지점과 약간 미묘하게 다릅니다.
— David Rodríguez-dribeas

2
C ++ 0x에서는 로컬 유형을 템플릿 매개 변수로 사용할 수 있습니다.
— tstenner

1
C ++ 0x를 사용하면 객체에 가상 함수 (예 : vtable)가 있으면 소멸자가 가상이됩니다.
— Macke

NRVO를 잊지 마세요. 물론 프로그램 출력을 변경하지 않는 한 모든 최적화가 허용됩니다
— jk.

26

정의되지 않은 동작없이 예상되는 의미를 사용하여 모든 클래스의 보호 된 데이터 및 함수 멤버에 액세스 할 수 있습니다. 방법을 보려면 계속 읽으십시오. 이에 대한 결함 보고서 도 읽어보십시오 .

일반적으로 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);
}


26

또 다른 숨겨진 기능은 함수 포인터 또는 참조로 변환 할 수있는 클래스 객체를 호출 할 수 있다는 것입니다. 그 결과에 대해 과부하 해결이 수행되고 인수가 완벽하게 전달됩니다.

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
}

이를 "대리 호출 기능"이라고합니다.


1
그 결과에 대해 과부하 해결이 완료되었다고 말할 때 실제로 두 Functor로 변환 한 다음 과부하 해결을 의미합니까? 연산자 Func1 * () 및 연산자 Func2 * ()에서 인쇄를 시도했지만 호출 할 변환 연산자를 파악할 때 올바른 것을 선택하는 것 같습니다.
— 네비게이터

3
@navigator, 네, 개념적으로 둘 다로 변환 한 다음 가장 좋은 것을 선택합니다. 결과 유형에서 이미 무엇을 산출할지 알고 있기 때문에 실제로 호출 할 필요가 없습니다. 실제 호출은 최종적으로 선택된 것이 밝혀지면 수행됩니다.
— Johannes Schaub-litb

26

숨겨진 기능 :

  1. 순수 가상 기능을 구현할 수 있습니다. 일반적인 예는 순수 가상 소멸자입니다.
  2. 함수가 예외 사양에 나열되지 않은 예외를 throw하지만 함수 std::bad_exception에 예외 사양이있는 경우 예외가로 변환되어 std::bad_exception자동으로 throw됩니다. 그렇게하면 최소한 a bad_exception가 던져 졌다는 것을 알 수 있습니다. 여기에서 자세한 내용을 읽어보십시오 .

  3. 기능 시도 블록

  4. 클래스 템플릿에서 typedef를 명확하게하는 템플릿 키워드. 멤버 템플릿 특수화의 이름은 후에 나타나는 경우 ., ->또는 ::운영자, 그 이름은 명시 적으로 규정 템플릿 매개 변수, 키워드 템플릿 접두사 멤버 템플릿 이름이 있습니다. 여기에서 자세한 내용을 읽어보십시오 .

  5. 함수 매개 변수 기본값은 런타임에 변경할 수 있습니다. 여기에서 자세한 내용을 읽어보십시오 .

  6. A[i] 잘 작동 i[A]

  7. 클래스의 임시 인스턴스를 수정할 수 있습니다! 상수가 아닌 멤버 함수는 임시 개체에서 호출 할 수 있습니다. 예를 들면 :

    struct Bar {
      void modify() {}
    }
    int main (void) {
      Bar().modify();   /* non-const function invoked on a temporary. */
    }

    여기에서 자세한 내용을 읽어보십시오 .

  8. :삼항 ( ?:) 연산자 표현식 의 앞뒤에 두 개의 다른 유형이있는 경우 표현식의 결과 유형은 둘 중 가장 일반적인 유형입니다. 예를 들면 :

    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)
    }

P Daddy : A [i] == * (A + i) == * (i + A) == i [A]
— abelenky

나는 정류를 얻었습니다. 이것은 [] 자체의 의미 론적 값이없고 단순히 "x [y]"가 "(* ((x) + (y ))) ". 내가 기대했던 것과는 전혀 다릅니다. 왜 이렇게 정의되었는지 궁금합니다.
— P Daddy

C와의 역 호환성
— jmucchiello

2
첫 번째 요점에 관하여 : 순수 가상 기능 을 구현 해야하는 한 가지 특별한 경우가 있습니다 : 순수 가상 소멸자.
— Frerich Raabe

24

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 ++ 프로그래머가 이것을 모른다는 것에 놀랐다.


11
반대쪽 끝에는 const 맵에서 operator []를 사용할 수 없습니다
— David Rodríguez-dribeas

2
닉에게 +1, 사람들은에 대해 모르는 경우 미쳐 버릴 수 있습니다 .find().
— LiraNuna

또는 " const map::operator[]오류 메시지를 생성합니다"
— 단지 누군가

2
언어의 기능이 아니라 표준 템플릿 라이브러리의 기능입니다. operator []가 유효한 참조를 반환하기 때문에 매우 분명합니다.
— Ramon Zarazua B.

2
나는 이것이 기능이라는 것을 깨닫기 위해지도가 그런 방식으로 작동하지 않는 한동안 C #에서지도를 사용해야했다. 사용했던 것보다 더 짜증이 났다고 생각했는데 틀린 것 같습니다. C #에서 누락되었습니다.
— sbi

20

이름없는 네임 스페이스에 함수 나 변수를 넣으면를 사용 static하여 파일 범위로 제한 할 수 없습니다.


"deprecates"는 강력한 용어입니다…
— Potatoswatter

@Potato : 예전의 의견이지만, 표준은 네임 스페이스 범위에서 정적 사용이 더 이상 사용되지 않으며 명명되지 않은 네임 스페이스를 선호한다고 말합니다.
— GManNickG 2010

@GMan : 문제가 없습니다. SO 페이지가 정말 "죽는"것 같지 않습니다. 이야기의 양쪽 모두를 위해 static글로벌 범위에서 어떤 식 으로든 사용되지 않습니다. (참고 : C ++ 03 §D.2)
— Potatoswatter

아, 자세히 읽어 보면 "글로벌 네임 스페이스에 선언 된 이름에는 글로벌 네임 스페이스 범위 (글로벌 범위라고도 함)가 있습니다." 그게 정말 의미하는 건가요?
— Potatoswatter 2010

@ 감자 : 응. :) staticuse는 클래스 유형 또는 함수 내에서만 사용해야합니다.
— GManNickG 2010

19

클래스 템플릿에서 일반적인 친구 함수를 정의하려면 특별한주의가 필요합니다.

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 에서이 섹션을 붙여 넣었습니다 .


18

void 함수는 void 값을 반환 할 수 있습니다.

거의 알려지지 않았지만 다음 코드는 괜찮습니다.

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; !
};

17

파일을 문자열 벡터로 읽어들입니다.

 vector<string> V;
 copy(istream_iterator<string>(cin), istream_iterator<string>(),
     back_inserter(V));

istream_iterator


8
또는 : vector <string> V ((istream_iterator <string> (cin)), istream_iterator <string>);
— UncleBens

5
당신은 의미 vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());- 두 번째 PARAM 후 괄호가 누락
— knittl

1
이것은 실제로 숨겨진 C ++ 기능이 아닙니다. 더 많은 STL 기능. STL! = 언어
— Nick Bedford

14

비트 필드를 템플릿 할 수 있습니다.

template <size_t X, size_t Y>
struct bitfield
{
    char left  : X;
    char right : Y;
};

나는 아직 이것에 대한 어떤 목적도 생각해 내지 못했지만, 정말 놀랐습니다.


1
내가 최근에 n 비트 산술을 위해 제안한 여기를 참조하십시오. stackoverflow.com/questions/8309538/…
— sehe

14

모든 프로그래밍 언어 중 가장 흥미로운 문법 중 하나입니다.

이 중 세 가지는 함께 속하고 두 가지는 완전히 다른 것입니다 ...

SomeType t = u;
SomeType t(u);
SomeType t();
SomeType t;
SomeType t(SomeType(u));

세 번째와 다섯 번째를 제외한 모든 SomeType객체는 스택에 객체를 정의 하고 초기화합니다 ( u처음 두 경우에는 기본 생성자가 있고 네 번째 경우에는 기본 생성자가 있습니다. 세 번째는 매개 변수를받지 않고를 반환하는 함수를 선언합니다 SomeType. 다섯 번째는 비슷하게 선언합니다. SomeType라는 유형의 값으로 하나의 매개 변수를 취하는 함수 u.


1st와 2nd 사이에 차이가 있습니까? 그래도 둘 다 초기화라는 것을 알고 있습니다.
— Özgür

Comptrol : 그렇게 생각하지 않습니다. 둘 다 복사 생성자를 호출하게 될 것입니다. 첫 번째는 할당 연산자처럼 보이지만 실제로는 복사 생성자입니다.
— abelenky

1
u가 SomeType과 다른 유형이면 첫 번째는 변환 생성자를 먼저 호출 한 다음 복사 생성자를 호출하는 반면 두 번째는 변환 생성자 만 호출합니다.
— Eclipse

3
첫 번째는 생성자의 암시 적 호출이고 두 번째는 명시 적 호출입니다. 차이점을 확인하려면 다음 코드를 살펴보십시오. #include <iostream> class sss {public : explicit sss (int) {std :: cout << "int"<< std :: endl; }; sss (double) {std :: cout << "double"<< std :: endl; }; }; int main () {sss ddd (7); // int 생성자를 호출합니다. sss xxx = 7; // 이중 생성자를 호출합니다. return 0; }
— Kirill V. Lyadvinsky 2009-06-23

True-생성자가 명시 적으로 선언되면 첫 번째 줄이 작동하지 않습니다.
— Eclipse

12

전방 선언 제거 :

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;

2
FStruct s = {};더 짧습니다.
— Constantin

마지막 예제에서는 다음과 같이 더 간단합니다. a (); 비(); float c = 1.0f;
— Zifre 2009

2
이 구문은 "float c = (a (), b (), 1.0f);" assigment-operation ( "c"의 지원)을 강조하는 데 유용합니다. 어시 그먼트 연산은 사용되지 않는 IMO가 될 가능성이 적기 때문에 프로그래밍에서 중요합니다. 이유를 모르겠다. 프로그램 상태가 모든 프레임에 다시 할당되는 함수형 프로그래밍과 관련이있을 수 있습니다. 추신. 그리고 아니오, "int d = (11,22,1.0f)"는 "1"과 같습니다. 1 분 전에 VS2008로 테스트했습니다.
— AareP

2
+1 전화 해야하지 main않나요? global().main();싱글 톤에 대해 제안 하고 잊어 버릴 것입니다 ( 임시로 작업 할 수 있으며 수명이 연장됨 )
— sehe

1
참조 할당이 이식 가능한지 의심 스럽습니다. 나는 앞으로 선언을 포기하는 구조체를 좋아합니다.
— Thomas Eding 2012

12

삼항 조건부 연산자 ?:는 두 번째 및 세 번째 피연산자가 "동의 할 수있는"유형 (비공식적으로 말함)을 가져야합니다. 그러나이 요구 사항에는 한 가지 예외 (의도 된 말장난)가 있습니다. 두 번째 또는 세 번째 피연산자는 void다른 피연산자의 유형에 관계없이 throw 식 (type ) 이 될 수 있습니다 .

즉, ?:연산자를 사용하여 다음과 같이 유효하게 유효한 C ++ 표현식을 작성할 수 있습니다.

i = a > b ? a : throw something();

BTW, throw 표현식이 실제로 는 표현식 (유형의 void)이고 문이 아니라는 사실 은 C ++ 언어의 또 다른 잘 알려지지 않은 기능입니다. 이것은 무엇보다도 다음 코드가 완벽하게 유효 함을 의미합니다.

void foo()
{
  return throw something();
}

이런 식으로 수행하는 데 많은 의미가 없지만 (일부 일반적인 템플릿 코드에서는 유용 할 수 있습니다).


그만한 가치에 대해 Neil은 추가 정보를 위해 stackoverflow.com/questions/1212978/… 에 대한 질문이 있습니다.
— GManNickG

12

지배 규칙은 유용하지만 거의 알려지지 않았습니다. 기본 클래스 격자를 통한 고유하지 않은 경로에 있더라도 멤버가 가상 기본 클래스에 속하면 부분적으로 숨겨진 멤버에 대한 이름 조회가 고유하다고 말합니다.

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 이름, 정적 / 비가 상 멤버 및 기타 모든 것에 적용됩니다. 메타 프로그램에서 덮어 쓸 수있는 특성을 구현하는 데 사용되는 것을 보았습니다.


1
산뜻한. struct C당신의 예에 포함 된 특별한 이유는 ...? 건배.
— Tony Delroy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.