C ++ 11의 새로운 구문“= default”
나는 왜 내가 할 것인지 할 것인지 이해하지 않습니다.
struct S {
int a;
S(int aa) : a(aa) {}
S() = default;
};
왜 그렇게 말하지 않습니까?
S() {} // instead of S() = default;
왜 새로운 구문을 가져 오나요?
기본 기본 생성자는 초기화 목록이없고 빈 복합 명령문이있는 사용자 정의 기본 생성자와 동일하게 정의됩니다.
§12.1 / 6 [class.ctor] 주소로 삭제 된 사용되지 않는 기본 기본 생성자 클래스 유형의 선언을 만들기 위해 odr-used를하거나 첫 번째 선언 할 때 명시 적으로 정의됩니다. 내재적으로 정의 된 기본 생성자는 ctor-initializer (12.6.2)가없고 빈 복합 명령문이없는 해당 클래스에 대해 사용자 작성 기본 생성자가 수행하는 클래스의 초기화 세트를 수행합니다. [...]
그러나 두 생성자 모두 동일하게 작동하지만 빈 구현을 제공하면 클래스의 일부 속성에 영향을줍니다. 정의 생성자를 user-제공하면 아무 것도 수행하지 않아도 유형이 집계 되지 않고 사소 하지 않습니다 . 클래스가 존재하거나 사소한 유형 (또는 일시적 유형, 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
) 구문을 새로 사용하면 의도를 명시 적으로 지정하여 코드를 쉽게 읽을 수 있습니다.
여러 가지 문제가 있습니다.
- 생성자 정의가 결합됩니다. 발송할 것입니다.
- 소멸자는 기본적으로 다형성 클래스에 적합하지 않습니다.
- 거부가 거절,이를 거절 수단이 없습니다.
- 기본 구현은 종종 수동으로 지정된 구현보다 더 효율적입니다.
- 기본이 아닌 구현은 사소하지 않으며 유형 의미에 영향을 미칩니다. 예를 들어 유형을 POD가 아닌 것으로 만듭니다.
- (사소하지 않은) 대체를 선언하지 않고 특수 멤버 함수 나 전역 연산자를 금지하는 방법은 없습니다.
type::type() = default; type::type() { x = 3; }
경우에 따라 추가 멤버 선언으로 기본값이 변경되기 때문에 멤버 함수 정의를 변경하지 않고도 클래스 본문을 변경할 수 있습니다.
참조 규칙의-세 규칙의-다섯 C ++ 11이됩니까? :
이동 생성자 및 이동 할당 연산자는 다른 특수 멤버 함수를 명시 적으로 선언하는 클래스에 대해 생성되지 않으며 이동 생성자 또는 이동을 명시 적으로 선언하는 클래스에 대해서는 복사 생성자와 복사 할당 연산자가 생성되지 않습니다. 할당 연산자, 그리고 명시 적으로 선언 된 소멸자와 암시 적으로 정의 된 복사 생성자 또는 암시 적으로 정의 된 복사 할당 연산자가있는 클래스는 더 이상 사용되지 않는 것으로 간주됩니다.
어떤 경우에는 의미론의 문제입니다. 기본 생성자에서는 그다지 명확하지 않지만 다른 컴파일러 생성 멤버 함수에서는 분명해집니다.
기본 생성자의 경우, 본문이 비어있는 기본 생성자를 사용하는 것과 같이 사소한 생성자가 될 후보로 간주 할 수 =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
기본 생성자 지원 이 한 가지 예입니다. C ++ 11의 주제 중 하나 인 =default
다른 모든 특수 키워드로 함수를 마크 업 하는 것 보다 정신적으로 사용하기 가 =default
더 쉽습니다. 언어를 더 쉽게 만듭니다. 여전히 많은 사마귀와 역 호환 타협이 있지만 사용 편의성 측면에서 C ++ 03에서 크게 발전한 것은 분명합니다.
차이점을 보여주는 예가 있습니다.
#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 + Value initialization
cout << x << endl;
return 0;
}
산출:
5
0
우리가 볼 수 있듯이 빈 A () 생성자에 대한 호출은 멤버를 초기화하지 않지만 B ()는 초기화합니다.
참고URL : https://stackoverflow.com/questions/20828907/the-new-syntax-default-in-c11
'IT' 카테고리의 다른 글
cmd.exe에서 stderr을 null로 리디렉션하는 방법 (0) | 2020.07.27 |
---|---|
HTML5shiv vs Dean Edwards IE7-js vs Modernizr- 어떤 것을 선택해야합니까? (0) | 2020.07.26 |
js에 API 호출을 수행하는 올바른 방법은 무엇입니까? (0) | 2020.07.26 |
ggplot2를 사용하여 R에서 투명한 배경으로 그래픽을 만드는 방법은 무엇입니까? (0) | 2020.07.26 |
비 관계형 데이터베이스 디자인 (0) | 2020.07.26 |