C ++ 11의 새로운 구문“= 기본값”


136

나는 왜 내가 이것을 할 것인지 이해하지 못한다.

struct S { 
    int a; 
    S(int aa) : a(aa) {} 
    S() = default; 
};

왜 그렇게 말하지 않습니까?

S() {} // instead of S() = default;

왜 새로운 구문을 가져 오나요?


30
Nitpick : default새로운 키워드가 아니며 이미 예약 된 키워드를 새로 사용한 것입니다.


수 MEY 이 질문은 당신에게 도움이 될 수 있습니다.
FreeNickname

7
다른 답변 외에도 '= default;'라고 주장합니다. 더 자기 문서화입니다.
Mark

답변:


136

기본 기본 생성자는 초기화 목록이없고 빈 복합 명령문이있는 사용자 정의 기본 생성자와 동일하게 정의됩니다.

§12.1 / 6 [class.ctor] 기본값으로 삭제 된 것으로 정의되지 않은 기본 생성자는 클래스 유형의 객체를 만들기 위해 odr-used를 사용하거나 첫 번째 선언 후 명시 적으로 기본값을 지정할 때 암시 적으로 정의됩니다. 내재적으로 정의 된 기본 생성자는 ctor-initializer (12.6.2)가없고 빈 복합 명령문이없는 해당 클래스에 대해 사용자 작성 기본 생성자가 수행하는 클래스의 초기화 세트를 수행합니다. [...]

그러나 두 생성자 모두 동일하게 작동하지만 빈 구현을 제공하면 클래스의 일부 속성에 영향을줍니다. 사용자 정의 생성자를 제공하면 아무 것도 수행하지 않아도 유형이 집계 되지 않고 사소 하지 않습니다 . 클래스가 집계 또는 사소한 유형 (또는 일시적 유형, POD 유형)이되도록하려면을 사용해야 = default합니다.

§8.5.1 / 1 [dcl.init.aggr] 집합은 사용자가 제공 한 생성자가없는 배열 또는 클래스입니다.

§12.1 / 5 [class.ctor] 기본 생성자는 사용자가 제공하지 않고 [...]

§9 / 6 [클래스] 사소한 클래스는 사소한 기본 생성자가 있고 [...]

시연하려면 :

#include <type_traits>

struct X {
    X() = default;
};

struct Y {
    Y() { };
};

int main() {
    static_assert(std::is_trivial<X>::value, "X should be trivial");
    static_assert(std::is_pod<X>::value, "X should be POD");
    
    static_assert(!std::is_trivial<Y>::value, "Y should not be trivial");
    static_assert(!std::is_pod<Y>::value, "Y should not be POD");
}

또한 constexpr암시 적 생성자가 있었을 경우 생성자를 명시 적으로 기본값으로 지정 하여 암시 적 생성자가 가질 수있는 것과 동일한 예외 사양을 제공합니다. 주어진 경우 암시 적 생성자는 constexpr(데이터 멤버가 초기화되지 않은 상태로 남아 있기 때문에) 되지 않았으며 빈 예외 사양도 있으므로 차이가 없습니다. 그러나 예, 일반적인 경우 수동으로 지정할 수 있습니다constexpr 암시 적 생성자와 일치하도록 예외 사양을 .

사용 = default은 복사 / 이동 생성자와 소멸자와 함께 사용할 수도 있기 때문에 약간의 균일 성을 가져옵니다. 예를 들어 빈 복사본 생성자는 기본 복사본 생성자와 동일하지 않습니다 (구성원의 멤버 별 복사를 수행함). 이러한 특수 멤버 함수 각각에 대해 = default(또는 = delete) 구문을 균일하게 사용하면 의도를 명시 적으로 지정하여 코드를 쉽게 읽을 수 있습니다.


거의. 12.1 / 6 : "사용자 작성 기본 생성자가 constexpr생성자 (7.1.5) 의 요구 사항을 충족하는 경우 암시 적으로 정의 된 기본 생성자는 constexpr입니다."
Casey

실제로 8.4.2 / 2는 더 유익합니다. "함수가 첫 번째 선언에서 명시 적으로 기본 설정되는 constexpr경우 (a) 암시 적 선언이 다음과 같은 경우 암시 적으로 간주됩니다. (b) 암시 적으로 동일한 것으로 간주됩니다. 암시 적으로 선언 된 것처럼 예외 사양 (15.4), ... "이 특정 경우에는 차이가 없지만 일반적으로에 foo() = default;비해 약간의 이점이 foo() {}있습니다.
케이시

2
당신은 차이가 없다고 말하고 차이점을 설명하기 위해 계속합니까?

@hvd이 경우, 암시 적 선언은 constexpr(데이터 멤버가 초기화되지 않은 상태이므로) 예외 스펙이 모든 예외를 허용 하기 때문에 차이가 없습니다 . 나는 그것을 더 명확하게 할 것입니다.
Joseph Mansfield

2
설명해 주셔서 감사합니다. 아직 함께하지만, 차이가있을 것처럼 보인다 constexpr(이 여기에 변화를 안 언급) : struct S1 { int m; S1() {} S1(int m) : m(m) {} }; struct S2 { int m; S2() = default; S2(int m) : m(m) {} }; constexpr S1 s1 {}; constexpr S2 s2 {};만이 s1오류를 준다 s2. clang과 g ++ 모두에서.

10

차이점을 보여주는 예가 있습니다.

#include <iostream>

using namespace std;
class A 
{
public:
    int x;
    A(){}
};

class B 
{
public:
    int x;
    B()=default;
};


int main() 
{ 
    int x = 5;
    new(&x)A(); // Call for empty constructor, which does nothing
    cout << x << endl;
    new(&x)B; // Call for default constructor
    cout << x << endl;
    new(&x)B(); // Call for default constructor + Value initialization
    cout << x << endl;
    return 0; 
} 

산출:

5
5
0

우리가 볼 수 있듯이 빈 A () 생성자에 대한 호출은 멤버를 초기화하지 않지만 B ()는 호출합니다.


7
이 구문을 설명해 주시겠습니까?-> new (& x) A ();
Vencat

5
우리는 새로운 메모리 할당 대신 변수 x의 주소에서 시작하여 메모리에 새로운 객체를 만들고 있습니다. 이 구문은 미리 할당 된 메모리에서 객체를 만드는 데 사용됩니다. 우리의 경우와 마찬가지로 B의 크기 = int의 크기이므로 new (& x) A ()는 x 변수 대신 새로운 객체를 생성합니다.
Slavenskij

설명해 주셔서 감사합니다.
Vencat

1
: 나는 GCC 8.3와 다른 결과를 얻을 ideone.com/XouXux
Adam.Er8

C ++ 14에서도 다른 결과가 나옵니다
Mayank Bhushan

9

n2210 은 몇 가지 이유를 제공합니다.

기본값 관리에는 몇 가지 문제가 있습니다.

  • 생성자 정의가 결합됩니다. 생성자를 선언하면 기본 생성자가 표시되지 않습니다.
  • 소멸자는 기본적으로 다형성 클래스에 적합하지 않으므로 명시 적 정의가 필요합니다.
  • 디폴트가 억제되면,이를 부활시킬 수단이 없습니다.
  • 기본 구현은 종종 수동으로 지정된 구현보다 더 효율적입니다.
  • 기본이 아닌 구현은 사소한 것이 아니며 형식 의미에 영향을 미칩니다. 예를 들어 형식을 비 POD로 만듭니다.
  • (사소한) 대용을 선언하지 않고 특수 멤버 함수 또는 전역 연산자를 금지 할 수단은 없습니다.

type::type() = default;
type::type() { x = 3; }

경우에 따라 기본 멤버가 추가 멤버 선언으로 변경되므로 멤버 함수 정의를 변경하지 않고도 클래스 본문이 변경 될 수 있습니다.

참조 규칙의-세 규칙의-다섯 C ++ 11이됩니까? :

다른 특수 멤버 함수를 명시 적으로 선언하는 클래스에 대해서는 이동 생성자 및 이동 대입 연산자가 생성되지 않으며, 이동 생성자 또는 이동을 명시 적으로 선언하는 클래스에 대해서는 복사 생성자 및 복사 대입 연산자가 생성되지 않습니다. 할당 연산자, 명시 적으로 선언 된 소멸자와 암시 적으로 정의 된 복사 생성자 또는 암시 적으로 정의 된 복사 할당 연산자가있는 클래스는 더 이상 사용되지 않는 것으로 간주됩니다.


1
그들은 가진 이유입니다 = default일반적으로보다는 일을하는 이유 = default일 대 생성자에 { }.
Joseph Mansfield

이후 @JosephMansfield 사실,하지만 {}이미 도입 이전에 언어의 특징이었다 =default, 이러한 이유는 않습니다 구분에 의존 암시 적으로 (예를 들어 그 의미 "부활 [억제 기본]에 아무런 수단이 없다" {}입니다 하지 기본에 해당 ).
Kyle Strand

7

경우에 따라 의미론의 문제입니다. 기본 생성자는 명확하지 않지만 다른 컴파일러 생성 멤버 함수에서는 분명합니다.

기본 생성자의 경우 빈 본문이있는 기본 생성자를 사용하는 것처럼 간단한 생성자가 될 수 있습니다 =default. 결국, 오래된 빈 기본 생성자는 적법한 C ++ 입니다.

struct S { 
  int a; 
  S() {} // legal C++ 
};

컴파일러가이 생성자를 사소한 것으로 이해하는지 여부는 대부분의 경우 최적화 외부 (수동 또는 컴파일러) 외부와 관련이 없습니다.

그러나 이렇게하면 빈 함수 본문을 "기본"으로 처리하려고하면 다른 유형의 멤버 함수에 대해 완전히 분류됩니다. 복사 생성자를 고려하십시오.

struct S { 
  int a; 
  S() {}
  S(const S&) {} // legal, but semantically wrong
};

위의 경우 빈 본문으로 작성된 복사 생성자가 잘못되었습니다 . 더 이상 실제로는 아무것도 복사하지 않습니다. 이것은 기본 복사 생성자 의미와는 매우 다른 의미의 집합입니다. 원하는 동작을하려면 코드를 작성해야합니다.

struct S { 
  int a; 
  S() {}
  S(const S& src) : a(src.a) {} // fixed
};

그러나이 간단한 경우에도 컴파일러가 복사 생성자가 자체 생성자와 동일한 지 확인하거나 복사 생성자가 사소한 것임을 확인하는 것은 훨씬 더 많은 부담이 됩니다.memcpy 기본적으로 ). 컴파일러는 각 멤버 이니셜 라이저 표현식을 확인하고 소스의 해당 멤버에 액세스하는 식과 동일한 지 확인해야하며, 기본 구성이 아닌 다른 멤버가 없는지 확인하십시오. 컴파일러는이 함수의 자체 생성 버전이 사소한 지 확인하는 데 사용합니다.

그런 다음 특히 사소하지 않은 경우에는 더 할당 할 수있는 복사 할당 연산자를 고려하십시오. 많은 클래스에 대해 작성하고 싶지는 않지만 C ++ 03에서는 강제로 사용됩니다.

struct T { 
  std::shared_ptr<int> b; 
  T(); // the usual definitions
  T(const T&);
  T& operator=(const T& src) {
    if (this != &src) // not actually needed for this simple example
      b = src.b; // non-trivial operation
    return *this;
};

이것은 간단한 경우이지만, T(특히 우리가 작업을 믹스로 이동 시키면) 간단한 유형으로 작성하고 싶었던 것보다 이미 더 많은 코드 입니다. 빈 바디는 이미 완벽하게 유효하고 명확한 의미를 갖기 때문에 "디폴트 채우기"를 의미하는 빈 바디에 의존 할 수 없습니다. 실제로 빈 본문이 "기본값 채우기"를 표시하는 데 사용 된 경우 no-op 복사 생성자 등을 명시 적으로 만들 수있는 방법이 없습니다.

다시 일관성의 문제입니다. 빈 본문은 "아무것도하지 않음"을 의미하지만 복사 생성자와 같은 것들에 대해서는 "아무것도하지 않음"을 원하지 않고 오히려 "억제되지 않은 경우 일반적으로하는 모든 것을하십시오"를 의미합니다. 따라서 =default. 그것은의 필요에 복사 / 이동 생성자와 대입 연산자처럼 억제 컴파일러가 생성 멤버 함수를 극복. 그런 다음 기본 생성자에서도 작동하게하는 것은 "분명한"것입니다.

빈 몸체를 가진 기본 생성자를 만드는 것이 좋을 수도 있고, 사소한 멤버 / 기본 생성자 =default를 오래된 코드를 더 최적으로 만드는 경우 와 마찬가지로 사소한 것으로 간주 되지만 사소한 코드에 의존하는 대부분의 저수준 코드 최적화를위한 기본 생성자도 간단한 복사 생성자에 의존합니다. 이전의 모든 복사 생성자를 "고정"해야하는 경우, 모든 이전 기본 생성자를 모두 수정해야하는 것은 그리 쉬운 일이 아닙니다. =default의도를 나타 내기 위해 명시 적을 사용하는 것이 훨씬 명확하고 분명 합니다.

컴파일러에서 생성 한 멤버 함수가 지원을 위해 명시 적으로 변경해야 할 몇 가지 사항이 있습니다. constexpr기본 생성자를 지원 하는 것이 한 예입니다. =default다른 모든 특수 키워드 를 사용 하여 함수를 마크 업 하는 것보다 =defaultC ++ 11의 테마 중 하나 인 기능을 표시 하는 것 보다 정신적으로 사용 하는 것이 더 쉽습니다. 언어를 더 쉽게 만듭니다. 여전히 많은 사마귀와 역전 타협이 있지만 사용 편의성에 관해서는 C ++ 03에서 크게 발전한 것이 분명합니다.


나는 기대에 문제가 있었다 = default할 것을a=0; 하고 아니었다! 나는 그것을 찬성하여 포기해야했다 : a(0). 나는 여전히 = defaulttho가 얼마나 유용한 지 혼란 스럽 습니다. 성능에 관한 것입니까? 내가 사용하지 않으면 어딘가에 깨질 = default까요? 나는 여기에있는 모든 대답을 읽으려고 노력했다. 나는 C ++에 익숙하지 않다. 그리고 그것을 이해하는 데 많은 어려움을 겪고있다.
물병 자리 힘

@AquariusPower : 성능에 대한 "단지"가 아니라 예외 및 기타 의미론과 관련하여 필요한 경우도 있습니다. 즉, 기본 연산자는 사소한 것일 수 있지만 기본값이 아닌 연산자는 사소한 것일 수 없으며 일부 코드는 메타 프로그래밍 기술을 사용하여 사소한 작업에 대한 동작을 변경하거나 유형을 허용하지 않습니다. 귀하의 a=0예를 때문에 별도의 (관련이기는하지만) 주제입니다 사소한 유형의 동작이다.
Sean Middleditch

그것이 가능하다는 것을 의미합니까? = default 여전히 부여가 a될 것입니다 =0? 어떤 식으로? "생성자 = default가 있고 필드가 올바르게 초기화 되는 방법을 부여 하는 방법"과 같은 새로운 질문을 만들 수 있다고 생각 합니까? btw a에서 문제가 발생 struct하지 않았 class으며 앱을 사용하지 않아도 올바르게 실행되고 = default있습니다. 그것이 좋은 질문이라면 그 질문에 최소한의 구조체를 추가하십시오 :)
Aquarius Power

1
@AquariusPower : 비 정적 데이터 멤버 이니셜 라이저를 사용할 수 있습니다. 구조체 struct { int a = 0; };를 다음 과 같이 작성하십시오 . 생성자가 필요하다고 결정하면 기본 생성 할 수 있지만 유형이 사소 하지는 않습니다 (괜찮습니다).
Sean Middleditch

2

사용 중단 std::is_pod및 대안 으로 인해 std::is_trivial && std::is_standard_layout@JosephMansfield의 답변은 다음과 같습니다.

#include <type_traits>

struct X {
    X() = default;
};

struct Y {
    Y() {}
};

int main() {
    static_assert(std::is_trivial_v<X>, "X should be trivial");
    static_assert(std::is_standard_layout_v<X>, "X should be standard layout");

    static_assert(!std::is_trivial_v<Y>, "Y should not be trivial");
    static_assert(std::is_standard_layout_v<Y>, "Y should be standard layout");
}

(가) 있습니다 Y아직 표준 레이아웃입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.