C++20에는 다음과 같은 새로운 언어 기능이 포함되어 있습니다.
C++20에는 다음과 같은 새로운 라이브러리 기능이 포함되어 있습니다.
C++17에는 다음과 같은 새로운 언어 기능이 포함되어 있습니다.
C++17에는 다음과 같은 새로운 라이브러리 기능이 포함되어 있습니다.
C++14에는 다음과 같은 새로운 언어 기능이 포함되어 있습니다.
C++14에는 다음과 같은 새로운 라이브러리 기능이 포함되어 있습니다.
C++11에는 다음과 같은 새로운 언어 기능이 포함되어 있습니다.
C++11에는 다음과 같은 새로운 라이브러리 기능이 포함되어 있습니다.
참고: 이 예제에서는 기본 수준에서 코루틴을 사용하는 방법을 설명하지만 코드를 컴파일할 때 더 많은 작업이 진행됩니다. 이 예제는 C++20의 코루틴을 완전히 포괄하는 것은 아닙니다.
generator
와task
클래스는 아직 표준 라이브러리에서 제공되지 않으므로 cppcoro 라이브러리를 사용하여 이러한 예제를 컴파일했습니다.
코루틴은 실행을 일시 중지하고 재개할 수 있는 특수 함수입니다. 코루틴을 정의하려면 co_return
, co_await
또는 co_yield
키워드가 함수 본문에 있어야 합니다. C++20의 코루틴은 스택이 없습니다. 컴파일러에 의해 최적화되지 않는 한 해당 상태는 힙에 할당됩니다.
코루틴의 예는 각 호출에서 값을 생성(생성)하는 생성기 함수입니다.
generator< int > range ( int start, int end) {
while (start < end) {
co_yield start;
start++;
}
// Implicit co_return at the end of this function:
// co_return;
}
for ( int n : range( 0 , 10 )) {
std::cout << n << std::endl;
}
위의 range
생성기 함수는 start
부터 end
(제외) 값을 생성하며, 각 반복 단계에서는 start
에 저장된 현재 값을 생성합니다. 생성기는 range
호출할 때마다 상태를 유지합니다(이 경우 호출은 for 루프의 각 반복에 대해 이루어집니다). co_yield
주어진 표현식을 취하고, 그 값을 산출(즉, 반환)하고, 그 시점에서 코루틴을 정지합니다. 재개되면 co_yield
이후에도 실행이 계속됩니다.
코루틴의 또 다른 예는 작업 이 대기할 때 실행되는 비동기 계산인 task 입니다.
task< void > echo (socket s) {
for (;;) {
auto data = co_await s. async_read ();
co_await async_write (s, data);
}
// Implicit co_return at the end of this function:
// co_return;
}
이 예에서는 co_await
키워드가 도입되었습니다. 이 키워드는 표현식을 취하고 기다리고 있는 것(이 경우 읽기 또는 쓰기)이 준비되지 않은 경우 실행을 일시 중단하고, 그렇지 않으면 실행을 계속합니다. (내부적으로 co_yield
co_await
사용합니다.)
작업을 사용하여 값을 느리게 평가합니다.
task< int > calculate_meaning_of_life () {
co_return 42 ;
}
auto meaning_of_life = calculate_meaning_of_life();
// ...
co_await meaning_of_life; // == 42
개념은 유형을 제한하는 컴파일 타임 조건자로 명명됩니다. 이는 다음과 같은 형식을 취합니다.
template < template-parameter-list >
concept concept-name = constraint-expression;
여기서 constraint-expression
constexpr 부울로 평가됩니다. 제약 조건은 유형이 숫자인지 해시 가능한지 여부와 같은 의미론적 요구 사항을 모델링해야 합니다. 주어진 유형이 바인딩된 개념을 충족하지 않으면 컴파일러 오류가 발생합니다(즉, constraint-expression
false
반환함). 제약 조건은 컴파일 타임에 평가되므로 보다 의미 있는 오류 메시지와 런타임 안전성을 제공할 수 있습니다.
// `T` is not limited by any constraints.
template < typename T>
concept always_satisfied = true ;
// Limit `T` to integrals.
template < typename T>
concept integral = std::is_integral_v<T>;
// Limit `T` to both the `integral` constraint and signedness.
template < typename T>
concept signed_integral = integral<T> && std::is_signed_v<T>;
// Limit `T` to both the `integral` constraint and the negation of the `signed_integral` constraint.
template < typename T>
concept unsigned_integral = integral<T> && !signed_integral<T>;
개념을 적용하기 위한 다양한 구문 형식이 있습니다.
// Forms for function parameters:
// `T` is a constrained type template parameter.
template <my_concept T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
requires my_concept<T>
void f (T v);
// `T` is a constrained type template parameter.
template < typename T>
void f (T v) requires my_concept<T>;
// `v` is a constrained deduced parameter.
void f (my_concept auto v);
// `v` is a constrained non-type template parameter.
template <my_concept auto v>
void g ();
// Forms for auto-deduced variables:
// `foo` is a constrained auto-deduced value.
my_concept auto foo = ...;
// Forms for lambdas:
// `T` is a constrained type template parameter.
auto f = []<my_concept T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> requires my_concept<T> (T v) {
// ...
};
// `T` is a constrained type template parameter.
auto f = []< typename T> (T v) requires my_concept<T> {
// ...
};
// `v` is a constrained deduced parameter.
auto f = [](my_concept auto v) {
// ...
};
// `v` is a constrained non-type template parameter.
auto g = []<my_concept auto v> () {
// ...
};
requires
키워드는 requires
절이나 requires
표현식을 시작하는 데 사용됩니다.
template < typename T>
requires my_concept<T> // `requires` clause.
void f (T);
template < typename T>
concept callable = requires (T f) { f (); }; // `requires` expression.
template < typename T>
requires requires (T x) { x + x; } // `requires` clause and expression on same line.
T add (T a, T b) {
return a + b;
}
requires
표현식의 매개변수 목록은 선택사항입니다. requires
표현식의 각 요구 사항은 다음 중 하나입니다.
template < typename T>
concept callable = requires (T f) { f (); };
typename
키워드가 표시되며, 주어진 유형 이름이 유효함을 나타냅니다. struct foo {
int foo;
};
struct bar {
using value = int ;
value data;
};
struct baz {
using value = int ;
value data;
};
// Using SFINAE, enable if `T` is a `baz`.
template < typename T, typename = std:: enable_if_t <std::is_same_v<T, baz>>>
struct S {};
template < typename T>
using Ref = T&;
template < typename T>
concept C = requires {
// Requirements on type `T`:
typename T::value; // A) has an inner member named `value`
typename S<T>; // B) must have a valid class template specialization for `S`
typename Ref<T>; // C) must be a valid alias template substitution
};
template <C T>
void g (T a);
g (foo{}); // ERROR: Fails requirement A.
g (bar{}); // ERROR: Fails requirement B.
g (baz{}); // PASS.
template < typename T>
concept C = requires(T x) {
{*x} -> std::convertible_to< typename T::inner>; // the type of the expression `*x` is convertible to `T::inner`
{x + 1 } -> std::same_as< int >; // the expression `x + 1` satisfies `std::same_as<decltype((x + 1))>`
{x * 1 } -> std::convertible_to<T>; // the type of the expression `x * 1` is convertible to `T`
};
requires
키워드로 표시되며 추가 제약 조건(예: 로컬 매개 변수 인수에 대한 제약 조건)을 지정합니다. template < typename T>
concept C = requires(T x) {
requires std::same_as< sizeof (x), size_t >;
};
참조: 개념 라이브러리.
C++20에서는 상용구를 줄이고 개발자가 보다 명확한 비교 의미 체계를 정의하는 데 도움이 되는 비교 함수를 작성하는 새로운 방법으로 우주선 연산자( <=>
)를 도입했습니다. 3방향 비교 연산자를 정의하면 다른 비교 연산자 함수(예: ==
, !=
, <
등)가 자동 생성됩니다.
세 가지 주문이 도입되었습니다.
std::strong_ordering
: 강력한 순서는 동일한 항목(동일성 및 상호 교환 가능)을 구별합니다. less
, greater
, equivalent
및 equal
순서를 제공합니다. 비교의 예: 목록에서 특정 값 검색, 정수 값, 대소문자 구분 문자열 검색.std::weak_ordering
: 약한 순서는 동일한 항목을 구별합니다(동일하지는 않지만 비교 목적으로 상호 교환 가능). less
, greater
및 equivalent
순서를 제공합니다. 비교의 예: 대소문자를 구분하지 않는 문자열, 정렬, 클래스의 표시 가능한 일부 멤버가 아닌 일부 멤버 비교.std::partial_ordering
: 부분 순서 지정은 약한 순서 지정과 동일한 원칙을 따르지만 순서 지정이 불가능한 경우도 포함합니다. less
, greater
, equivalent
및 순서 unordered
순서를 제공합니다. 비교의 예: 부동 소수점 값(예: NaN
).기본 3방향 비교 연산자는 멤버별 비교를 수행합니다.
struct foo {
int a;
bool b;
char c;
// Compare `a` first, then `b`, then `c` ...
auto operator <=>( const foo&) const = default ;
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == true
f1 == f2; // == false
f1 >= f2; // == false
자신만의 비교를 정의할 수도 있습니다.
struct foo {
int x;
bool b;
char c;
std::strong_ordering operator <=>( const foo& other) const {
return x <=> other. x ;
}
};
foo f1{ 0 , false , ' a ' }, f2{ 0 , true , ' b ' };
f1 < f2; // == false
f1 == f2; // == true
f1 >= f2; // == true
C 스타일 지정 초기화 구문. 지정된 초기화 목록에 명시적으로 나열되지 않은 모든 멤버 필드는 기본적으로 초기화됩니다.
struct A {
int x;
int y;
int z = 123 ;
};
A a {. x = 1 , . z = 2 }; // a.x == 1, a.y == 0, a.z == 2
람다 식에 익숙한 템플릿 구문을 사용합니다.
auto f = []< typename T>(std::vector<T> v) {
// ...
};
이 기능은 일반적인 코드 패턴을 단순화하고 범위를 엄격하게 유지하며 일반적인 수명 문제에 대한 우아한 솔루션을 제공합니다.
for ( auto v = std::vector{ 1 , 2 , 3 }; auto & e : v) {
std::cout << e;
}
// prints "123"
레이블이 지정된 명령문이 실행될 확률이 높다는 힌트를 최적화 프로그램에 제공합니다.
switch (n) {
case 1 :
// ...
break ;
[[likely]] case 2 : // n == 2 is considered to be arbitrarily more
// ... // likely than any other value of n
break ;
}
가능성/가능성 없음 속성 중 하나가 if 문의 오른쪽 괄호 뒤에 나타나는 경우 해당 분기에서 해당 하위 명령문(본문)이 실행될 가능성/가능성이 없음을 나타냅니다.
int random = get_random_number_between_x_and_y( 0 , 3 );
if (random > 0 ) [[likely]] {
// body of if statement
// ...
}
반복문의 하위문(본문)에도 적용될 수 있습니다.
while (unlikely_truthy_condition) [[unlikely]] {
// body of while statement
// ...
}
[=]
사용하여 람다 캡처에서 this
암시적으로 캡처하는 것은 이제 더 이상 사용되지 않습니다. [=, this]
또는 [=, *this]
사용하여 명시적으로 캡처하는 것을 선호합니다.
struct int_value {
int n = 0 ;
auto getter_fn () {
// BAD:
// return [=]() { return n; };
// GOOD:
return [=, * this ]() { return n; };
}
};
이제 유형이 아닌 템플릿 매개변수에서 클래스를 사용할 수 있습니다. 템플릿 인수로 전달된 객체는 const T
유형을 가지며, 여기서 T
객체 유형이며 정적 저장 기간을 갖습니다.
struct foo {
foo () = default ;
constexpr foo ( int ) {}
};
template <foo f = {}>
auto get_foo () {
return f;
}
get_foo (); // uses implicit constructor
get_foo<foo{ 123 }>();
이제 가상 함수를 constexpr
만들고 컴파일 타임에 평가할 수 있습니다. constexpr
가상 함수는 constexpr
아닌 가상 함수를 재정의할 수 있으며 그 반대의 경우도 마찬가지입니다.
struct X1 {
virtual int f () const = 0;
};
struct X2 : public X1 {
constexpr virtual int f () const { return 2 ; }
};
struct X3 : public X2 {
virtual int f () const { return 3 ; }
};
struct X4 : public X3 {
constexpr virtual int f () const { return 4 ; }
};
constexpr X4 x4;
x4.f(); // == 4
생성자가 명시적으로 지정되는지 여부를 컴파일 타임에 조건부로 선택합니다. explicit(true)
explicit
지정하는 것과 같습니다.
struct foo {
// Specify non-integral types (strings, floats, etc.) require explicit construction.
template < typename T>
explicit (!std::is_integral_v<T>) foo(T) {}
};
foo a = 123 ; // OK
foo b = " 123 " ; // ERROR: explicit constructor is not a candidate (explicit specifier evaluates to true)
foo c { " 123 " }; // OK
constexpr
함수와 유사하지만 consteval
지정자가 있는 함수는 상수를 생성해야 합니다. 이를 immediate functions
라고 합니다.
consteval int sqr ( int n) {
return n * n;
}
constexpr int r = sqr( 100 ); // OK
int x = 100 ;
int r2 = sqr(x); // ERROR: the value of 'x' is not usable in a constant expression
// OK if `sqr` were a `constexpr` function
가독성을 높이기 위해 열거형의 멤버를 범위로 가져옵니다. 전에:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel channel) {
switch (channel) {
case rgba_color_channel::red: return " red " ;
case rgba_color_channel::green: return " green " ;
case rgba_color_channel::blue: return " blue " ;
case rgba_color_channel::alpha: return " alpha " ;
}
}
후에:
enum class rgba_color_channel { red, green, blue, alpha };
std::string_view to_string (rgba_color_channel my_channel) {
switch (my_channel) {
using enum rgba_color_channel;
case red: return " red " ;
case green: return " green " ;
case blue: return " blue " ;
case alpha: return " alpha " ;
}
}
값별로 매개변수 팩을 캡처합니다.
template < typename ... Args>
auto f (Args&&... args){
// BY VALUE:
return [... args = std::forward<Args>(args)] {
// ...
};
}
참조로 매개변수 팩을 캡처합니다.
template < typename ... Args>
auto f (Args&&... args){
// BY REFERENCE:
return [&... args = std::forward<Args>(args)] {
// ...
};
}
UTF-8 문자열을 표현하기 위한 표준 유형을 제공합니다.
char8_t utf8_str[] = u8" u0123 " ;
constinit
지정자를 사용하려면 컴파일 타임에 변수를 초기화해야 합니다.
const char * g () { return " dynamic initialization " ; }
constexpr const char * f ( bool p) { return p ? " constant initializer " : g (); }
constinit const char * c = f( true ); // OK
constinit const char * d = g( false ); // ERROR: `g` is not constexpr, so `d` cannot be evaluated at compile-time.
가변 매크로가 비어 있지 않은 경우 주어진 인수를 평가하여 가변 매크로를 지원하는 데 도움이 됩니다.
# define F (...) f( 0 __VA_OPT__ (,) __VA_ARGS__)
F(a, b, c) // replaced by f(0, a, b, c)
F() // replaced by f(0)
보다 복잡한 개념을 구축하기 위해 표준 라이브러리에서도 개념을 제공합니다. 이들 중 일부는 다음과 같습니다:
핵심 언어 개념:
same_as
- 두 가지 유형이 동일함을 지정합니다.derived_from
- 유형이 다른 유형에서 파생됨을 지정합니다.convertible_to
- 유형이 암시적으로 다른 유형으로 변환될 수 있음을 지정합니다.common_with
- 두 유형이 공통 유형을 공유하도록 지정합니다.integral
- 유형이 정수 계열 유형임을 지정합니다.default_constructible
- 특정 유형의 객체가 기본 생성될 수 있음을 지정합니다.비교 개념:
boolean
- 부울 컨텍스트에서 유형을 사용할 수 있음을 지정합니다.equality_comparable
- operator==
가 동등 관계임을 지정합니다.개체 개념:
movable
- 특정 유형의 객체를 이동하고 교체할 수 있음을 지정합니다.copyable
- 특정 유형의 객체를 복사, 이동 및 교체할 수 있음을 지정합니다.semiregular
- 특정 유형의 객체가 복사, 이동, 교체 및 기본 생성될 수 있음을 지정합니다.regular
- 유형이 일반 임을 지정합니다. 즉, semiregular
이면서 equality_comparable
임을 지정합니다.호출 가능한 개념:
invocable
- 주어진 인수 유형 세트를 사용하여 호출 가능 유형을 호출할 수 있음을 지정합니다.predicate
- 호출 가능 유형이 부울 조건자임을 지정합니다.참조: 개념.
printf
의 단순성과 iostream
의 유형 안전성을 결합합니다. 중괄호를 자리 표시자로 사용하고 printf 스타일 지정자와 유사한 사용자 정의 형식을 지원합니다.
std::format ( " {1} {0} " , " world " , " hello " ); // == "hello world"
int x = 123 ;
std::string str = std::format( " x: {} " , x); // str == "x: 123"
// Format to an output iterator:
for ( auto x : { 1 , 2 , 3 }) {
std::format_to (std::ostream_iterator< char >{std::cout, " n " }, " {} " , x);
}
사용자 정의 유형의 형식을 지정하려면 다음을 수행하십시오.
struct fraction {
int numerator;
int denominator;
};
template <>
struct std ::formatter<fraction>
{
constexpr auto parse (std::format_parse_context& ctx) {
return ctx. begin ();
}
auto format ( const fraction& f, std::format_context& ctx) const {
return std::format_to (ctx. out (), " {0:d}/{1:d} " , f. numerator , f. denominator );
}
};
fraction f{ 1 , 2 };
std::format ( " {} " , f); // == "1/2"
동기화(즉, 출력 인터리빙 없음)를 보장하는 래핑된 출력 스트림에 대한 출력 작업을 버퍼링합니다.
std::osyncstream{std::cout} << " The value of x is: " << x << std::endl;
범위는 인접한 요소 그룹에 대한 범위 확인 액세스를 제공하는 컨테이너의 뷰(즉, 비소유)입니다. 뷰는 자신의 요소를 소유하지 않기 때문에 생성하고 복사하는 비용이 저렴합니다. 뷰에 대해 생각하는 단순화된 방법은 뷰가 해당 데이터에 대한 참조를 보유하고 있다는 것입니다. 포인터/반복자 및 길이 필드를 유지하는 것과 달리 범위는 두 필드를 단일 객체로 래핑합니다.
범위는 동적으로 크기가 조정되거나 고정 크기로 지정될 수 있습니다( 범위 라고 함). 고정 크기 범위는 경계 검사를 통해 이점을 얻습니다.
Span은 const를 전파하지 않으므로 읽기 전용 범위를 구성하려면 std::span<const T>
사용하세요.
예: 동적으로 크기가 조정되는 범위를 사용하여 다양한 컨테이너에서 정수를 인쇄합니다.
void print_ints (std::span< const int > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_ints (std::vector{ 1 , 2 , 3 });
print_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 });
int a[ 10 ] = { 0 };
print_ints (a);
// etc.
예: 정적으로 크기가 조정된 범위는 범위 범위와 일치하지 않는 컨테이너에 대해 컴파일되지 않습니다.
void print_three_ints (std::span< const int , 3 > ints) {
for ( const auto n : ints) {
std::cout << n << std::endl;
}
}
print_three_ints (std::vector{ 1 , 2 , 3 }); // ERROR
print_three_ints (std::array< int , 5 >{ 1 , 2 , 3 , 4 , 5 }); // ERROR
int a[ 10 ] = { 0 };
print_three_ints (a); // ERROR
std::array< int , 3 > b = { 1 , 2 , 3 };
print_three_ints (b); // OK
// You can construct a span manually if required:
std::vector c{ 1 , 2 , 3 };
print_three_ints (std::span< const int , 3 >{ c. data (), 3 }); // OK: set pointer and length field.
print_three_ints (std::span< const int , 3 >{ c. cbegin (), c. cend () }); // OK: use iterator pairs.
C++20은 popcount를 포함한 일부 비트 연산을 제공하는 새로운 <bit>
헤더를 제공합니다.
std::popcount ( 0u ); // 0
std::popcount ( 1u ); // 1
std::popcount ( 0b1111'0000u ); // 4
<numbers>
헤더에 정의된 PI, 오일러 수 등의 수학 상수입니다.
std::numbers:: pi ; // 3.14159...
std::numbers::e; // 2.71828...
컴파일 타임 컨텍스트에서 호출될 때 진실인 조건자 함수입니다.
constexpr bool is_compile_time () {
return std::is_constant_evaluated ();
}
constexpr bool a = is_compile_time(); // true
bool b = is_compile_time(); // false
auto p = std::make_shared< int []>( 5 ); // pointer to `int[5]`
// OR
auto p = std::make_shared< int [ 5 ]>(); // pointer to `int[5]`
문자열(및 문자열 뷰)에는 이제 문자열이 주어진 문자열로 시작하거나 끝나는지 확인하기 위한 starts_with
및 ends_with
멤버 함수가 있습니다.
std::string str = " foobar " ;
str.starts_with( " foo " ); // true
str.ends_with( " baz " ); // false
세트 및 맵과 같은 연관 컨테이너에는 "반복기 끝 찾기 및 확인" 관용어 대신 사용할 수 있는 contains
멤버 함수가 있습니다.
std::map< int , char > map {{ 1 , ' a ' }, { 2 , ' b ' }};
map.contains( 2 ); // true
map.contains( 123 ); // false
std::set< int > set { 1 , 2 , 3 };
set.contains( 2 ); // true
객체를 한 유형에서 다른 유형으로 재해석하는 더 안전한 방법입니다.
float f = 123.0 ;
int i = std::bit_cast< int >(f);
오버플로 없이 두 정수의 중간점을 안전하게 계산합니다.
std::midpoint ( 1 , 3 ); // == 2
주어진 배열/"유사 배열" 객체를 std::array
로 변환합니다.
std::to_array ( " foo " ); // returns `std::array<char, 4>`
std::to_array< int >({ 1 , 2 , 3 }); // returns `std::array<int, 3>`
int a[] = { 1 , 2 , 3 };
std::to_array (a); // returns `std::array<int, 3>`
처음 N개의 인수(여기서 N은 std::bind_front
에 대한 지정된 함수 뒤의 인수 수)를 지정된 자유 함수, 람다 또는 멤버 함수에 바인딩합니다.
const auto f = []( int a, int b, int c) { return a + b + c; };
const auto g = std::bind_front(f, 1 , 1 );
g ( 1 ); // == 3
문자열, 목록, 벡터, 맵 등과 같은 다양한 STL 컨테이너에 대해 std::erase
및/또는 std::erase_if
제공합니다.
값으로 지우려면 std::erase
사용하고 요소를 지울 시기를 지정하려면 std::erase_if
사용하세요. 두 함수 모두 지워진 요소의 수를 반환합니다.
std::vector v{ 0 , 1 , 0 , 2 , 0 , 3 };
std::erase (v, 0 ); // v == {1, 2, 3}
std::erase_if (v, []( int n) { return n == 0 ; }); // v == {1, 2, 3}
비교 결과에 이름을 부여하는 도우미 함수:
std::is_eq ( 0 <=> 0 ); // == true
std::is_lteq ( 0 <=> 1 ); // == true
std::is_gt ( 0 <=> 1 ); // == false
3방향 비교도 참조하세요.
3방향 비교를 사용하여 두 범위를 사전식으로 비교하고 적용 가능한 가장 강력한 비교 범주 유형의 결과를 생성합니다.
std::vector a{ 0 , 0 , 0 }, b{ 0 , 0 , 0 }, c{ 1 , 1 , 1 };
auto cmp_ab = std::lexicographical_compare_three_way(
a.begin(), a.end(), b.begin(), b.end());
std::is_eq (cmp_ab); // == true
auto cmp_ac = std::lexicographical_compare_three_way(
a.begin(), a.end(), c.begin(), c.end());
std::is_lt (cmp_ac); // == true
3방향 비교, 3방향 비교 도우미도 참조하세요.
함수에 수행되는 방식과 매우 유사하지만 이제 클래스 생성자를 포함하는 자동 템플릿 인수 추론입니다.
template < typename T = float >
struct MyContainer {
T val;
MyContainer () : val{} {}
MyContainer (T val) : val{val} {}
// ...
};
MyContainer c1 { 1 }; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>
auto
의 추론 규칙에 따라 허용되는 유형의 비유형 템플릿 매개변수 목록[*]을 존중하면서 템플릿 인수는 해당 인수의 유형에서 추론될 수 있습니다.
template < auto ... seq>
struct my_integer_sequence {
// Implementation here ...
};
// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence< int , 0 , 1 , 2 >();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence< 0 , 1 , 2 >();
* - 예를 들어, double
을 템플릿 매개변수 유형으로 사용할 수 없으며, 이로 인해 auto
사용한 유효하지 않은 추론이 됩니다.
접기 표현식은 이항 연산자를 통해 템플릿 매개변수 팩의 접기를 수행합니다.
op
접기 연산자이고 e
확장되지 않은 매개변수 팩인 (... op e)
또는 (e op ...)
형식의 표현식을 단항 접기 라고 합니다.op
접기 연산자인 (e1 op ... op e2)
형식의 표현을 이진 접기 라고 합니다. e1
또는 e2
확장되지 않은 매개변수 팩이지만 둘 다는 아닙니다. template < typename ... Args>
bool logicalAnd (Args... args) {
// Binary folding.
return ( true && ... && args);
}
bool b = true ;
bool & b2 = b;
logicalAnd (b, b2, true ); // == true
template < typename ... Args>
auto sum (Args... args) {
// Unary folding.
return (... + args);
}
sum ( 1.0 , 2 . 0f , 3 ); // == 6.0
균일한 초기화 구문과 함께 사용될 때 auto
추론으로 변경됩니다. 이전에는 auto x {3};
std::initializer_list<int>
추론하고 이제 int
로 추론합니다.
auto x1 { 1 , 2 , 3 }; // error: not a single element
auto x2 = { 1 , 2 , 3 }; // x2 is std::initializer_list<int>
auto x3 { 3 }; // x3 is int
auto x4 { 3.0 }; // x4 is double
constexpr
사용한 컴파일 타임 람다.
auto identity = []( int n) constexpr { return n; };
static_assert (identity( 123 ) == 123);
constexpr auto add = []( int x, int y) {
auto L = [=] { return x; };
auto R = [=] { return y; };
return [=] { return L () + R (); };
};
static_assert (add( 1 , 2 )() == 3);
constexpr int addOne ( int n) {
return [n] { return n + 1 ; }();
}
static_assert (addOne( 1 ) == 2);
this
값으로 캡처합니다. 이전에는 람다 환경에서 this
캡처하는 것이 참조용이었습니다. 이것이 문제가 되는 예는 잠재적으로 수명이 지난 객체를 사용할 수 있어야 하는 콜백을 사용하는 비동기 코드입니다. *this
(C++17)는 이제 현재 개체의 복사본을 만드는 반면 this
(C++11)는 계속해서 참조로 캡처합니다.
struct MyObj {
int value { 123 };
auto getValueCopy () {
return [* this ] { return value; };
}
auto getValueRef () {
return [ this ] { return value; };
}
};
MyObj mo;
auto valueCopy = mo.getValueCopy();
auto valueRef = mo.getValueRef();
mo.value = 321 ;
valueCopy (); // 123
valueRef (); // 321
인라인 지정자는 함수뿐만 아니라 변수에도 적용될 수 있습니다. 인라인으로 선언된 변수는 인라인으로 선언된 함수와 동일한 의미를 갖습니다.
// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{ 321 }; // mov esi, dword ptr [x1]
// x1: .long 321
S x2 = S{ 123 }; // mov eax, dword ptr [.L_ZZ4mainE2x2]
// mov dword ptr [rbp - 8], eax
// .L_ZZ4mainE2x2: .long 123
또한 소스 파일에서 초기화할 필요가 없도록 정적 멤버 변수를 선언하고 정의하는 데 사용할 수도 있습니다.
struct S {
S () : id{count++} {}
~S () { count--; }
int id;
static inline int count{ 0 }; // declare and initialize count to 0 within the class
};
네임스페이스 확인 연산자를 사용하여 중첩된 네임스페이스 정의를 만듭니다.
namespace A {
namespace B {
namespace C {
int i;
}
}
}
위의 코드는 다음과 같이 작성할 수 있습니다.
namespace A ::B::C {
int i;
}
auto [ x, y, z ] = expr;
쓰기를 허용하는 구조 분해 초기화에 대한 제안입니다. 여기서 expr
의 유형은 튜플과 유사한 객체였으며, 그 요소는 변수 x
, y
및 z
(이 구성이 선언하는)에 바인딩됩니다. 튜플 유사 객체 에는 std::tuple
, std::pair
, std::array
및 집계 구조가 포함됩니다.
using Coordinate = std::pair< int , int >;
Coordinate origin () {
return Coordinate{ 0 , 0 };
}
const auto [ x, y ] = origin();
x; // == 0
y; // == 0
std::unordered_map<std::string, int > mapping {
{ " a " , 1 },
{ " b " , 2 },
{ " c " , 3 }
};
// Destructure by reference.
for ( const auto & [key, value] : mapping) {
// Do something with key and value
}
일반적인 코드 패턴을 단순화하고 사용자가 범위를 엄격하게 유지하는 데 도움이 되는 if
및 switch
문의 새 버전입니다.
{
std::lock_guard<std::mutex> lk (mx);
if (v. empty ()) v. push_back (val);
}
// vs.
if (std::lock_guard<std::mutex> lk (mx); v.empty()) {
v. push_back (val);
}
Foo gadget (args);
switch ( auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
// vs.
switch (Foo gadget (args); auto s = gadget.status()) {
case OK: gadget. zip (); break ;
case Bad: throw BadFoo (s. message ());
}
컴파일 타임 조건에 따라 인스턴스화되는 코드를 작성합니다.
template < typename T>
constexpr bool isIntegral () {
if constexpr (std::is_integral<T>::value) {
return true ;
} else {
return false ;
}
}
static_assert (isIntegral< int >() == true);
static_assert (isIntegral< char >() == true);
static_assert (isIntegral< double >() == false);
struct S {};
static_assert (isIntegral<S>() == false);
u8
로 시작하는 문자 리터럴은 char
유형의 문자 리터럴입니다. UTF-8 문자 리터럴의 값은 ISO 10646 코드 포인트 값과 같습니다.
char x = u8 ' x ' ;
이제 중괄호 구문을 사용하여 열거형을 초기화할 수 있습니다.
enum byte : unsigned char {};
byte b { 0 }; // OK
byte c {- 1 }; // ERROR
byte d = byte{ 1 }; // OK
byte e = byte{ 256 }; // ERROR
C++17에는 [[fallthrough]]
, [[nodiscard]]
및 [[maybe_unused]]
세 가지 새로운 속성이 도입되었습니다.
[[fallthrough]]
switch 문에서 실패하는 것이 의도된 동작임을 컴파일러에 나타냅니다. 이 속성은 스위치 문에서만 사용할 수 있으며 다음 케이스/기본 레이블 앞에 배치되어야 합니다. switch (n) {
case 1 :
// ...
[[fallthrough]];
case 2 :
// ...
break ;
case 3 :
// ...
[[fallthrough]];
default :
// ...
}
[[nodiscard]]
함수나 클래스에 이 속성이 있고 해당 반환 값이 삭제되면 경고를 발행합니다. [[nodiscard]] bool do_something () {
return is_success; // true for success, false for failure
}
do_something (); // warning: ignoring return value of 'bool do_something()',
// declared with attribute 'nodiscard'
// Only issues a warning when `error_info` is returned by value.
struct [[nodiscard]] error_info {
// ...
};
error_info do_something () {
error_info ei;
// ...
return ei;
}
do_something (); // warning: ignoring returned value of type 'error_info',
// declared with attribute 'nodiscard'
[[maybe_unused]]
변수나 매개변수가 사용되지 않을 수 있으며 의도된 것임을 컴파일러에 나타냅니다. void my_callback (std::string msg, [[maybe_unused]] bool error) {
// Don't care if `msg` is an error message, just log it.
log (msg);
}
__has_include (operand)
연산자를 #if
및 #elif
표현식에 사용하여 헤더나 소스 파일( operand
)을 포함할 수 있는지 여부를 확인할 수 있습니다.
이에 대한 한 가지 사용 사례는 동일한 방식으로 작동하는 두 개의 라이브러리를 사용하고, 선호하는 라이브러리를 시스템에서 찾을 수 없는 경우 백업/실험 라이브러리를 사용하는 것입니다.
# ifdef __has_include
# if __has_include(<optional>)
# include < optional >
# define have_optional 1
# elif __has_include(<experimental/optional>)
# include < experimental/optional >
# define have_optional 1
# define experimental_optional
# else
# define have_optional 0
# endif
# endif
또한 프로그램이 어떤 플랫폼에서 실행되고 있는지 알지 못한 채 다양한 플랫폼에서 다른 이름이나 위치에 존재하는 헤더를 포함하는 데 사용할 수 있습니다. OpenGL 헤더는 macOS의 OpenGL
디렉토리와 다른 플랫폼의 GL
디렉토리에 있는 좋은 예입니다. 플랫폼.
# ifdef __has_include
# if __has_include(<OpenGL/gl.h>)
# include < OpenGL/gl.h >
# include < OpenGL/glu.h >
# elif __has_include(<GL/gl.h>)
# include < GL/gl.h >
# include < GL/glu.h >
# else
# error No suitable OpenGL headers found.
# endif
# endif
CTAD( 클래스 템플릿 인수 공제 )를 사용하면 컴파일러가 생성자 인수에서 템플릿 인수를 추론할 수 있습니다.
std::vector v{ 1 , 2 , 3 }; // deduces std::vector<int>
std::mutex mtx;
auto lck = std::lock_guard{ mtx }; // deduces to std::lock_guard<std::mutex>
auto p = new std::pair{ 1.0 , 2.0 }; // deduces to std::pair<double, double>*
사용자 정의 유형의 경우 추론 가이드를 사용하여 해당되는 경우 템플릿 인수를 추론하는 방법을 컴파일러에 안내할 수 있습니다.
template < typename T>
struct container {
container (T t) {}
template < typename Iter>
container (Iter beg, Iter end);
};
// deduction guide
template < typename Iter>
container (Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
container a{ 7 }; // OK: deduces container<int>
std::vector< double > v{ 1.0 , 2.0 , 3.0 };
auto b = container{ v. begin (), v. end () }; // OK: deduces container<double>
container c{ 5 , 6 }; // ERROR: std::iterator_traits<int>::value_type is not a type
클래스 템플릿 std::variant
유형이 안전한 union
나타냅니다. 주어진 시간에 std::variant
인스턴스는 대체 유형 중 하나의 값을 보유합니다(값이 없을 수도 있음).
std::variant< int , double > v{ 12 };
std::get< int >(v); // == 12
std::get< 0 >(v); // == 12
v = 12.0 ;
std::get< double >(v); // == 12.0
std::get< 1 >(v); // == 12.0
클래스 템플릿 std::optional
선택적으로 포함된 값, 즉 존재할 수도 있고 존재하지 않을 수도 있는 값을 관리합니다. 선택 사항의 일반적인 사용 사례는 실패할 수 있는 함수의 반환 값입니다.
std::optional<std::string> create ( bool b) {
if (b) {
return " Godzilla " ;
} else {
return {};
}
}
create ( false ).value_or( " empty " ); // == "empty"
create ( true ).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if ( auto str = create( true )) {
// ...
}
모든 유형의 단일 값에 대한 유형이 안전한 컨테이너입니다.
std::any x { 5 };
x.has_value() // == true
std::any_cast< int >(x) // == 5
std::any_cast< int &>(x) = 10 ;
std::any_cast< int >(x) // == 10
문자열에 대한 비소유 참조입니다. 문자열 위에 추상화를 제공하는 데 유용합니다(예: 구문 분석).
// Regular strings.
std::string_view cppstr { " foo " };
// Wide strings.
std::wstring_view wcstr_v { L" baz " };
// Character arrays.
char array[ 3 ] = { ' b ' , ' a ' , ' r ' };
std::string_view array_v (array, std::size(array));
std::string str { " trim me " };
std::string_view v {str};
v.remove_prefix(std::min(v.find_first_not_of( " " ), v.size()));
str; // == " trim me"
v; // == "trim me"
매개변수를 사용하여 Callable
객체를 호출합니다. 호출 가능한 객체의 예로는 std::function
또는 람다가 있습니다. 일반 함수와 유사하게 호출할 수 있는 객체입니다.
template < typename Callable>
class Proxy {
Callable c_;
public:
Proxy (Callable c) : c_{ std::move (c) } {}
template < typename ... Args>
decltype ( auto ) operator ()(Args&&... args) {
// ...
return std::invoke (c_, std::forward<Args>(args)...);
}
};
const auto add = []( int x, int y) { return x + y; };
Proxy p{ add };
p ( 1 , 2 ); // == 3
인수 튜플을 사용하여 Callable
객체를 호출합니다.
auto add = []( int x, int y) {
return x + y;
};
std::apply (add, std::make_tuple( 1 , 2 )); // == 3
새로운 std::filesystem
라이브러리는 파일 시스템의 파일, 디렉터리 및 경로를 조작하는 표준 방법을 제공합니다.
여기에서는 사용 가능한 공간이 있는 경우 큰 파일이 임시 경로에 복사됩니다.
const auto bigFilePath { " bigFileToCopy " };
if (std::filesystem::exists(bigFilePath)) {
const auto bigFileSize { std::filesystem::file_size (bigFilePath)};
std::filesystem::path tmpPath { " /tmp " };
if ( std::filesystem::space (tmpPath). available > bigFileSize) {
std::filesystem::create_directory (tmpPath. append ( " example " ));
std::filesystem::copy_file (bigFilePath, tmpPath. append ( " newFile " ));
}
}
새로운 std::byte
유형은 데이터를 바이트로 표현하는 표준 방법을 제공합니다. char
또는 unsigned char
에 대해 std::byte
사용하면 문자 유형도 아니고 산술 유형도 아니라는 이점이 있습니다. 사용할 수 있는 유일한 연산자 오버로드는 비트 연산입니다.
std::byte a { 0 };
std::byte b { 0xFF };
int i = std::to_integer< int >(b); // 0xFF
std::byte c = a & b;
int j = std::to_integer< int >(c); // 0
std::byte
단순히 열거형이며 열거형의 직접 목록 초기화 덕분에 열거형의 중괄호 초기화가 가능해졌습니다.
비용이 많이 드는 복사, 이동 또는 힙 할당/할당 취소로 인한 오버헤드 없이 노드를 이동하고 컨테이너를 병합합니다.
한 맵에서 다른 맵으로 요소 이동:
std::map< int , string> src {{ 1 , " one " }, { 2 , " two " }, { 3 , " buckle my shoe " }};
std::map< int , string> dst {{ 3 , " three " }};
dst.insert(src.extract(src.find( 1 ))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract( 2 )); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };
전체 세트 삽입:
std::set< int > src { 1 , 3 , 5 };
std::set< int > dst { 2 , 4 , 5 };
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }
컨테이너보다 오래 지속되는 요소 삽입:
auto elementFactory () {
std::set<...> s;
s. emplace (...);
return s. extract (s. begin ());
}
s2.insert(elementFactory());
지도 요소의 키 변경:
std::map< int , string> m {{ 1 , " one " }, { 2 , " two " }, { 3 , " three " }};
auto e = m.extract( 2 );
e.key() = 4 ;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }
copy
, find
및 sort
메소드와 같은 많은 STL 알고리즘은 병렬 실행 정책인 seq
, par
및 par_unseq
지원하기 시작했습니다. 이는 "순차적", "병렬" 및 "병렬 비순차적"으로 변환됩니다.
std::vector< int > longVector;
// Find element using parallel execution policy
auto result1 = std::find(std::execution::par, std::begin(longVector), std::end(longVector), 2 );
// Sort elements using sequential execution policy
auto result2 = std::sort(std::execution::seq, std::begin(longVector), std::end(longVector));
모든 요소가 선택될 확률이 동일한 주어진 시퀀스(교체 없음)에서 n개의 요소를 샘플링합니다.
const std::string ALLOWED_CHARS = " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ;
std::string guid;
// Sample 5 characters from ALLOWED_CHARS.
std::sample (ALLOWED_CHARS.begin(), ALLOWED_CHARS.end(), std::back_inserter(guid),
5, std::mt19937{ std::random_device{}() });
std::cout << guid; // e.g. G1fW2
주어진 값을 하한과 상한 사이에 고정합니다.
std::clamp ( 42 , - 1 , 1 ); // == 1
std::clamp (- 42 , - 1 , 1 ); // == -1
std::clamp ( 0 , - 1 , 1 ); // == 0
// `std::clamp` also accepts a custom comparator:
std::clamp ( 0 , - 1 , 1 , std::less<>{}); // == 0
주어진 범위의 요소를 접습니다. 개념적으로 std::accumulate
와 유사하지만 std::reduce
접기를 병렬로 수행합니다. 접기가 병렬로 수행되기 때문에 이진 연산을 지정하는 경우 결합 및 교환이 가능해야 합니다. 또한 주어진 이항 연산은 요소를 변경하거나 주어진 범위 내의 반복자를 무효화해서는 안 됩니다.
기본 이진 연산은 초기 값이 0인 std::plus입니다.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::reduce (std::cbegin(a), std::cend(a)); // == 6
// Using a custom binary op:
std::reduce (std::cbegin(a), std::cend(a), 1, std::multiplies<>{}); // == 6
추가적으로 리듀서에 대한 변환을 지정할 수 있습니다:
std::transform_reduce (std::cbegin(a), std::cend(a), 0, std::plus<>{}, times_ten); // == 60
const std::array< int , 3 > b{ 1 , 2 , 3 };
const auto product_times_ten = []( const auto a, const auto b) { return a * b * 10 ; };
std::transform_reduce (std::cbegin(a), std::cend(a), std::cbegin(b), 0, std::plus<>{}, product_times_ten); // == 140
변환과 함께 접두사 합계(포함 및 배타적 스캔 모두)를 지원합니다.
const std::array< int , 3 > a{ 1 , 2 , 3 };
std::inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}); // 1 3 6
std::exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}); // 0 1 3
const auto times_ten = []( const auto n) { return n * 10 ; };
std::transform_inclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, std::plus<>{}, times_ten); // 10 30 60
std::transform_exclusive_scan (std::cbegin(a), std::cend(a),
std::ostream_iterator<int>{ std::cout, " " }, 0 , std::plus<>{}, times_ten); // 0 10 30
최대공약수(GCD)와 최소공배수(LCM).
const int p = 9 ;
const int q = 3 ;
std::gcd (p, q); // == 3
std::lcm (p, q); // == 9
주어진 함수의 결과에 대한 부정을 반환하는 유틸리티 함수입니다.
const std::ostream_iterator< int > ostream_it{ std::cout, " " };
const auto is_even = []( const auto n) { return n % 2 == 0 ; };
std::vector< int > v{ 0 , 1 , 2 , 3 , 4 };
// Print all even numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, is_even); // 0 2 4
// Print all odd (not even) numbers.
std::copy_if (std::cbegin(v), std::cend(v), ostream_it, std::not_fn(is_even)); // 1 3
적분과 부동 소수점을 문자열로 변환하거나 그 반대로 변환합니다. 변환은 발생하지 않고 할당되지 않으며 C 표준 라이브러리의 해당 변환보다 더 안전합니다.
사용자는 std::to_chars
에 필요한 충분한 저장 공간을 할당할 책임이 있습니다. 그렇지 않으면 반환 값에 오류 코드 개체를 설정하여 함수가 실패합니다.
이러한 함수를 사용하면 부동 유형 입력에 대한 기본(기본값은 10진수) 또는 형식 지정자를 선택적으로 전달할 수 있습니다.
std::to_chars
함수가 주어진 버퍼 내부에 쓴 문자열의 끝 부분에 있는 (비상수) char 포인터와 오류 코드 객체를 반환합니다.std::from_chars
성공 시 함수에 전달된 끝 포인터와 오류 코드 객체와 동일한 const char 포인터를 반환합니다.이 함수에서 반환된 두 오류 코드 개체는 모두 성공 시 기본으로 초기화된 오류 코드 개체와 동일합니다.
숫자 123
std::string
으로 변환합니다.
const int n = 123 ;
// Can use any container, string, array, etc.
std::string str;
str.resize( 3 ); // hold enough storage for each digit of `n`
const auto [ ptr, ec ] = std::to_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << str << std::endl; } // 123
else { /* handle failure */ }
값이 "123"
인 std::string
정수로 변환합니다.
const std::string str{ " 123 " };
int n;
const auto [ ptr, ec ] = std::from_chars(str.data(), str.data() + str.size(), n);
if (ec == std::errc{}) { std::cout << n << std::endl; } // 123
else { /* handle failure */ }
std::chrono::duration
및 std::chrono::time_point
에 대한 abs, round, ceil 및 Floor 도우미 기능을 제공합니다.
using seconds = std::chrono::seconds;
std::chrono::milliseconds d{ 5500 };
std::chrono::abs (d); // == 5s
std::chrono::round<seconds>(d); // == 6s
std::chrono::ceil<seconds>(d); // == 6s
std::chrono::floor<seconds>(d); // == 5s
이진 리터럴은 밑이 2인 숫자를 나타내는 편리한 방법을 제공합니다. '
로 숫자를 구분할 수 있습니다.
0b110 // == 6
0b1111'1111 // == 255
C++14에서는 이제 매개변수 목록에서 auto
유형 지정자를 허용하여 다형성 람다를 활성화합니다.
auto identity = []( auto x) { return x; };
int three = identity( 3 ); // == 3
std::string foo = identity( " foo " ); // == "foo"
이를 통해 임의의 표현식으로 초기화된 람다 캡처를 생성할 수 있습니다. 캡처된 값에 지정된 이름은 둘러싸는 범위의 변수와 관련될 필요가 없으며 람다 본문 내에 새 이름을 도입합니다. 초기화 표현식은 람다가 생성 될 때 평가됩니다( 호출 될 때가 아님).
int factory ( int i) { return i * 10 ; }
auto f = [x = factory( 2 )] { return x; }; // returns 20
auto generator = [x = 0 ] () mutable {
// this would not compile without 'mutable' as we are modifying x on each call
return x++;
};
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2
이전에는 복사 또는 참조로만 캡처할 수 있었던 값을 람다로 이동 (또는 전달 )하는 것이 이제 가능하므로 이제 값을 기준으로 람다의 이동 전용 유형을 캡처할 수 있습니다. 아래 예에서 =
왼쪽에 있는 task2
캡처 목록의 p
람다 본문 전용 새 변수이며 원래 p
참조하지 않습니다.
auto p = std::make_unique< int >( 1 );
auto task1 = [=] { *p = 5 ; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5 ; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created
이 참조 캡처를 사용하면 참조된 변수와 다른 이름을 가질 수 있습니다.
auto x = 1 ;
auto f = [&r = x, x = x * 10 ] {
++r;
return r + x;
};
f (); // sets x to 2 and returns 12
C++14의 auto
반환 유형을 사용하면 컴파일러는 유형을 추론하려고 시도합니다. 람다를 사용하면 이제 auto
사용하여 반환 유형을 추론할 수 있으므로 추론된 참조 또는 rvalue 참조 반환이 가능해집니다.
// Deduce return type as `int`.
auto f ( int i) {
return i;
}
template < typename T>
auto & f (T& t) {
return t;
}
// Returns a reference to a deduced type.
auto g = []( auto & x) -> auto & { return f (x); };
int y = 123 ;
int & z = g(y); // reference to `y`
decltype(auto)
유형 지정자는 auto
와 마찬가지로 유형을 추론합니다. 그러나 참조와 cv 한정자를 유지하면서 반환 유형을 추론하지만 auto
그렇지 않습니다.
const int x = 0 ;
auto x1 = x; // int
decltype ( auto ) x2 = x; // const int
int y = 0 ;
int & y1 = y;
auto y2 = y1; // int
decltype ( auto ) y3 = y1; // int&
int && z = 0 ;
auto z1 = std::move(z); // int
decltype ( auto ) z2 = std::move(z); // int&&
// Note: Especially useful for generic code!
// Return type is `int`.
auto f ( const int & i) {
return i;
}
// Return type is `const int&`.
decltype ( auto ) g( const int & i) {
return i;
}
int x = 123 ;
static_assert (std::is_same< const int &, decltype(f(x))>::value == 0);
static_assert (std::is_same< int , decltype(f(x))>::value == 1);
static_assert (std::is_same< const int &, decltype(g(x))>::value == 1);
참조: decltype (C++11)
.
C++11에서 constexpr
함수 본문에는 typedef
s, using
s 및 단일 return
문을 포함하여(이에 국한되지 않음) 매우 제한된 구문 세트만 포함될 수 있습니다. C++14에서는 허용되는 구문 세트가 if
문, 다중 return
, 루프 등과 같은 가장 일반적인 구문을 포함하도록 크게 확장되었습니다.
constexpr int factorial ( int n) {
if (n <= 1 ) {
return 1 ;
} else {
return n * factorial (n - 1 );
}
}
factorial ( 5 ); // == 120
C++14에서는 변수를 템플릿화할 수 있습니다.
template < class T >
constexpr T pi = T( 3.1415926535897932385 );
template < class T >
constexpr T e = T( 2.7182818284590452353 );
C++14에서는 단위(함수, 클래스 등)가 권장되지 않으며 컴파일 경고가 발생할 가능성이 있음을 나타내기 위해 [[deprecated]]
속성을 도입했습니다. 이유가 제공되면 경고에 포함됩니다.
[[deprecated]]
void old_method ();
[[deprecated( " Use new_method instead " )]]
void legacy_method ();
chrono
및 basic_string
에 대한 새로운 내장 리터럴을 포함하여 표준 라이브러리 유형에 대한 새로운 사용자 정의 리터럴입니다. 이는 컴파일 타임에 사용할 수 있음을 의미하는 constexpr
일 수 있습니다. 이러한 리터럴의 일부 용도에는 컴파일 시간 정수 구문 분석, 이진 리터럴 및 허수 리터럴이 포함됩니다.
using namespace std ::chrono_literals ;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440
클래스 템플릿 std::integer_sequence
컴파일 타임 정수 시퀀스를 나타냅니다. 위에는 몇 가지 도우미가 내장되어 있습니다.
std::make_integer_sequence<T, N>
- T
유형으로 0, ..., N - 1
의 시퀀스를 생성합니다.std::index_sequence_for<T...>
- 템플릿 매개변수 팩을 정수 시퀀스로 변환합니다.배열을 튜플로 변환합니다.
template < typename Array, std:: size_t ... I>
decltype ( auto ) a2t_impl( const Array& a, std::integer_sequence<std:: size_t , I...>) {
return std::make_tuple (a[I]...);
}
template < typename T, std:: size_t N, typename Indices = std::make_index_sequence<N>>
decltype ( auto ) a2t( const std::array<T, N>& a) {
return a2t_impl (a, Indices ());
}
std::make_unique
다음과 같은 이유로 std::unique_ptr
인스턴스를 생성하는 데 권장되는 방법입니다.
new
연산자를 사용하지 않아도 됩니다.foo
함수를 호출한다고 가정해 보겠습니다. foo (std::unique_ptr<T>{ new T{}}, function_that_throws(), std::unique_ptr<T>{ new T{}});
컴파일러는 new T{}
, function_that_throws()
등을 자유롭게 호출할 수 있습니다. T
의 첫 번째 구성에서 힙에 데이터를 할당했기 때문에 여기서 누출이 발생했습니다. std::make_unique
사용하면 예외 안전성이 제공됩니다.
foo (std::make_unique<T>(), function_that_throws(), std::make_unique<T>());
std::unique_ptr
및 std::shared_ptr
에 대한 자세한 내용은 스마트 포인터(C++11) 섹션을 참조하세요.
개체를 이동한다는 것은 해당 개체가 관리하는 일부 리소스의 소유권을 다른 개체로 이전하는 것을 의미합니다.
이동 의미론의 첫 번째 이점은 성능 최적화입니다. 임시적이거나 명시적으로 std::move
호출하여 객체의 수명이 거의 끝나갈 때 이동이 리소스를 전송하는 더 저렴한 방법인 경우가 많습니다. 예를 들어, std::vector
이동하는 것은 단지 일부 포인터와 내부 상태를 새 벡터에 복사하는 것입니다. 복사하려면 벡터에 포함된 모든 단일 요소를 복사해야 합니다. 이는 이전 벡터가 곧 복사될 경우 비용이 많이 들고 불필요합니다. 의해 파괴됨.
또한 이동을 통해 std::unique_ptr
s(스마트 포인터)와 같은 복사할 수 없는 유형이 한 번에 하나의 리소스 인스턴스만 관리되는 동시에 인스턴스를 전송할 수 있음을 언어 수준에서 보장할 수 있습니다. 범위 사이.
rvalue 참조, 이동 의미론을 위한 특수 멤버 함수, std::move
, std::forward
, forwarding references
섹션을 참조하세요.
C++11에는 rvalue 참조 라는 새로운 참조가 도입되었습니다. 템플릿 유형이 아닌 매개변수(예: int
또는 사용자 정의 유형)인 T
에 대한 rvalue 참조는 T&&
구문을 사용하여 생성됩니다. Rvalue 참조는 rvalue에만 바인딩됩니다.
lvalue와 rvalue를 사용한 유형 추론:
int x = 0 ; // `x` is an lvalue of type `int`
int & xl = x; // `xl` is an lvalue of type `int&`
int && xr = x; // compiler error -- `x` is an lvalue
int && xr2 = 0 ; // `xr2` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
void f ( int & x) {}
void f ( int && x) {}
f (x); // calls f(int&)
f (xl); // calls f(int&)
f ( 3 ); // calls f(int&&)
f (std::move(x)); // calls f(int&&)
f (xr2); // calls f(int&)
f (std::move(xr2)); // calls f(int&& x)
참조: std::move
, std::forward
, forwarding references
.
(비공식적으로) 범용 참조 라고도 합니다. 전달 참조는 T&&
구문(여기서 T
는 템플릿 유형 매개변수임)을 사용하거나 auto&&
사용하여 생성됩니다. 이를 통해 완벽한 전달이 가능합니다. 즉, 값 범주를 유지하면서 인수를 전달할 수 있는 기능입니다(예: lvalue는 lvalue로 유지되고 임시는 rvalue로 전달됩니다).
전달 참조를 사용하면 참조가 유형에 따라 lvalue 또는 rvalue에 바인딩될 수 있습니다. 전달 참조는 참조 축소 규칙을 따릅니다.
T& &
는 T&
됩니다T& &&
는 T&
됩니다T&& &
는 T&
됩니다T&& &&
는 T&&
가 됩니다. lvalue와 rvalue를 사용한 auto
유형 추론:
int x = 0 ; // `x` is an lvalue of type `int`
auto && al = x; // `al` is an lvalue of type `int&` -- binds to the lvalue, `x`
auto && ar = 0 ; // `ar` is an lvalue of type `int&&` -- binds to the rvalue temporary, `0`
lvalue 및 rvalue를 사용한 템플릿 유형 매개변수 추론:
// Since C++14 or later:
void f ( auto && t) {
// ...
}
// Since C++11 or later:
template < typename T>
void f (T&& t) {
// ...
}
int x = 0 ;
f ( 0 ); // T is int, deduces as f(int &&) => f(int&&)
f (x); // T is int&, deduces as f(int& &&) => f(int&)
int & y = x;
f (y); // T is int&, deduces as f(int& &&) => f(int&)
int && z = 0 ; // NOTE: `z` is an lvalue with type `int&&`.
f (z); // T is int&, deduces as f(int& &&) => f(int&)
f (std::move(z)); // T is int, deduces as f(int &&) => f(int&&)
std::move
, std::forward
, rvalue references
도 참조하세요.
...
구문은 매개변수 팩을 생성하거나 확장합니다. 템플릿 매개변수 팩 은 0개 이상의 템플릿 인수(비유형, 유형 또는 템플릿)를 허용하는 템플릿 매개변수입니다. 하나 이상의 매개변수 팩이 있는 템플릿을 가변 템플릿 이라고 합니다.
template < typename ... T>
struct arity {
constexpr static int value = sizeof ...(T);
};
static_assert (arity<>::value == 0 );
static_assert (arity< char , short , int >::value == 3 );
이에 대한 흥미로운 용도는 가변 함수 인수를 반복하기 위해 매개변수 팩 에서 초기화 목록을 생성하는 것입니다.
template < typename First, typename ... Args>
auto sum ( const First first, const Args... args) -> decltype(first) {
const auto values = {first, args...};
return std::accumulate (values. begin (), values. end (), First{ 0 });
}
sum ( 1 , 2 , 3 , 4 , 5 ); // 15
sum ( 1 , 2 , 3 ); // 6
sum ( 1.5 , 2.0 , 3.7 ); // 7.2
"중괄호 목록" 구문을 사용하여 생성된 요소의 경량 배열형 컨테이너입니다. 예를 들어, { 1, 2, 3 }
은 std::initializer_list<int>
유형의 정수 시퀀스를 생성합니다. 객체 벡터를 함수에 전달하는 대신 유용합니다.
int sum ( const std::initializer_list< int >& list) {
int total = 0 ;
for ( auto & e : list) {
total += e;
}
return total;
}
auto list = { 1 , 2 , 3 };
sum (list); // == 6
sum ({ 1 , 2 , 3 }); // == 6
sum ({}); // == 0
컴파일 타임에 평가되는 어설션입니다.
constexpr int x = 0 ;
constexpr int y = 1 ;
static_assert (x == y, " x != y " );
auto
유형의 변수는 초기화 프로그램의 유형에 따라 컴파일러에 의해 추론됩니다.
auto a = 3.14 ; // double
auto b = 1 ; // int
auto & c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto && e = 1 ; // int&&
auto && f = b; // int&
auto g = new auto ( 123 ); // int*
const auto h = 1 ; // const int
auto i = 1 , j = 2 , k = 3 ; // int, int, int
auto l = 1 , m = true , n = 1.61 ; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer
특히 복잡한 유형의 경우 가독성에 매우 유용합니다.
std::vector< int > v = ...;
std::vector< int >::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();
함수는 auto
사용하여 반환 유형을 추론할 수도 있습니다. C++11에서는 반환 유형을 명시적으로 지정하거나 다음과 같이 decltype
사용하여 지정해야 합니다.
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2 ); // == 3
add ( 1 , 2.0 ); // == 3.0
add ( 1.5 , 1.5 ); // == 3.0
위 예제의 후행 반환 유형은 x + y
표현식의 선언된 유형 ( decltype
섹션 참조)입니다. 예를 들어, x
가 정수이고 y
가 double이면 decltype(x + y)
는 double입니다. 따라서 위 함수는 x + y
표현식이 어떤 유형을 생성하는지에 따라 유형을 추론합니다. 후행 반환 유형은 해당 매개변수에 액세스할 수 있으며 적절한 경우 this
액세스할 수 있습니다.
lambda
는 범위 내 변수를 캡처할 수 있는 이름 없는 함수 개체입니다. 기능: 캡처 목록 ; 선택적 후행 반환 유형이 있는 선택적 매개변수 세트입니다. 그리고 시체. 캡처 목록의 예:
[]
- 아무것도 캡처하지 않습니다.[=]
- 값별로 범위 내 로컬 객체(로컬 변수, 매개변수)를 캡처합니다.[&]
- 범위 내에서 참조로 로컬 객체(로컬 변수, 매개변수)를 캡처합니다.[this]
- 참조로 this
캡처합니다.[a, &b]
- 객체 a
를 값으로, b
참조로 캡처합니다. int x = 1 ;
auto getX = [=] { return x; };
getX (); // == 1
auto addX = [=]( int y) { return x + y; };
addX ( 1 ); // == 2
auto getXRef = [&]() -> int & { return x; };
getXRef (); // int& to `x`
기본적으로 컴파일러 생성 메서드는 const
로 표시되므로 람다 내에서 값 캡처를 수정할 수 없습니다. mutable
키워드를 사용하면 캡처된 변수를 수정할 수 있습니다. 키워드는 매개변수 목록(비어 있어도 반드시 있어야 함) 뒤에 배치됩니다.
int x = 1 ;
auto f1 = [&x] { x = 2 ; }; // OK: x is a reference and modifies the original
auto f2 = [x] { x = 2 ; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x]() mutable { x = 2 ; }; // OK: the lambda can perform any operations on the captured value
decltype
은 전달된 표현식의 선언된 유형을 반환하는 연산자입니다. cv 한정자와 참조는 표현식의 일부인 경우 유지됩니다. decltype
의 예:
int a = 1 ; // `a` is declared as type `int`
decltype (a) b = a; // `decltype(a)` is `int`
const int & c = a; // `c` is declared as type `const int&`
decltype (c) d = a; // `decltype(c)` is `const int&`
decltype ( 123 ) e = 123; // `decltype(123)` is `int`
int && f = 1 ; // `f` is declared as type `int&&`
decltype (f) g = 1; // `decltype(f) is `int&&`
decltype ((a)) h = g; // `decltype((a))` is int&
template < typename X, typename Y>
auto add (X x, Y y) -> decltype(x + y) {
return x + y;
}
add ( 1 , 2.0 ); // `decltype(x + y)` => `decltype(3.0)` => `double`
참조: decltype(auto) (C++14)
.
그러나 typedef
사용하는 것과 의미상 유사하지만 using
유형 별칭은 읽기 쉽고 템플릿과 호환됩니다.
template < typename T>
using Vec = std::vector<T>;
Vec< int > v; // std::vector<int>
using String = std::string;
String s { " foo " };
C++11에는 C의 NULL
매크로를 대체하도록 설계된 새로운 널 포인터 유형이 도입되었습니다. nullptr
자체는 std::nullptr_t
유형이며 암시적으로 포인터 유형으로 변환될 수 있으며 NULL
과 달리 bool
제외하고 정수 유형으로 변환할 수 없습니다.
void foo ( int );
void foo ( char *);
foo ( NULL ); // error -- ambiguous
foo ( nullptr ); // calls foo(char*)
암시적 변환, 기본 유형을 지정할 수 없음, 범위 오염 등 C 스타일 열거형과 관련된 다양한 문제를 해결하는 유형 안전 열거형입니다.
// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000 , Green = 0xff00 , Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;
속성은 __attribute__(...)
, __declspec
등에 대한 범용 구문을 제공합니다.
// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f () {
throw " error " ;
}
일정한 표현식은 컴파일 타임에서 컴파일러에 의해 평가 될 수 있는 표현식입니다. 비 복합 계산만이 일정한 표현식으로 수행 될 수 있습니다 (이 규칙은 이후 버전에서 점차 완화됩니다). constexpr
지정자를 사용하여 변수, 함수 등이 일정한 표현식임을 나타냅니다.
constexpr int square ( int x) {
return x * x;
}
int square2 ( int x) {
return x * x;
}
int a = square( 2 ); // mov DWORD PTR [rbp-4], 4
int b = square2( 2 ); // mov edi, 2
// call square2(int)
// mov DWORD PTR [rbp-8], eax
이전 스 니펫에서는 square
호출 할 때 계산이 컴파일 타임에 수행 된 다음 결과가 코드 생성에 포함되며 square2
런타임에 호출됩니다.
constexpr
값은 컴파일러가 컴파일 타임에 평가할 수 있지만 보장되지는 않는 값입니다.
const int x = 123 ;
constexpr const int & y = x; // error -- constexpr variable `y` must be initialized by a constant expression
수업과의 지속적인 표현 :
struct Complex {
constexpr Complex ( double r, double i) : re{r}, im{i} { }
constexpr double real () { return re; }
constexpr double imag () { return im; }
private:
double re;
double im;
};
constexpr Complex I ( 0 , 1 );
생성자는 이제 이니셜 라이저 목록을 사용하여 같은 클래스에서 다른 생성자를 호출 할 수 있습니다.
struct Foo {
int foo;
Foo ( int foo) : foo{foo} {}
Foo () : Foo( 0 ) {}
};
Foo foo;
foo.foo; // == 0
사용자 정의 리터럴을 사용하면 언어를 확장하고 자신의 구문을 추가 할 수 있습니다. 문자를 만들려면 T operator "" X(...) { ... }
T
X
정의하십시오. 이 함수의 이름은 문자의 이름을 정의합니다. 밑줄로 시작하지 않는 문자 이름은 예약되어 있으며 호출되지 않습니다. 문자가 호출되는 유형에 따라 사용자 정의 리터럴 함수가 수용 해야하는 매개 변수에 대한 규칙이 있습니다.
섭씨를 화씨로 전환 :
// `unsigned long long` parameter required for integer literal.
long long operator " " _celsius( unsigned long long tempCelsius) {
return std::llround (tempCelsius * 1.8 + 32 );
}
24_celsius; // == 75
정수 변환으로 문자열 :
// `const char*` and `std::size_t` required as parameters.
int operator " " _int( const char * str, std:: size_t ) {
return std::stoi (str);
}
" 123 " _int; // == 123, with type `int`
가상 함수가 다른 가상 함수를 대체하도록 지정합니다. 가상 함수가 부모의 가상 함수를 무시하지 않으면 컴파일러 오류가 발생합니다.
struct A {
virtual void foo ();
void bar ();
};
struct B : A {
void foo () override ; // correct -- B::foo overrides A::foo
void bar () override ; // error -- A::bar is not virtual
void baz () override ; // error -- B::baz does not override A::baz
};
파생 클래스에서 가상 함수를 상환 할 수 없거나 클래스를 상속받을 수 없음을 지정합니다.
struct A {
virtual void foo ();
};
struct B : A {
virtual void foo () final ;
};
struct C : B {
virtual void foo (); // error -- declaration of 'foo' overrides a 'final' function
};
클래스는 상속받을 수 없습니다.
struct A final {};
struct B : A {}; // error -- base 'A' is marked 'final'
생성자와 같은 기능의 기본 구현을 제공하는보다 우아하고 효율적인 방법.
struct A {
A () = default ;
A ( int x) : x{x} {}
int x { 1 };
};
A a; // a.x == 1
A a2 { 123 }; // a.x == 123
상속과 :
struct B {
B () : x{ 1 } {}
int x;
};
struct C : B {
// Calls B::B
C () = default ;
};
C c; // c.x == 1
삭제 된 함수 구현을 제공하는보다 우아하고 효율적인 방법. 물체의 사본을 방지하는 데 유용합니다.
class A {
int x;
public:
A ( int x) : x{x} {};
A ( const A&) = delete ;
A& operator =( const A&) = delete ;
};
A x { 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted
컨테이너의 요소를 반복하기위한 구문 설탕.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int & x : a) x *= 2 ;
// a == { 2, 4, 6, 8, 10 }
int&
대조적으로 int
사용할 때의 차이점에 유의하십시오.
std::array< int , 5 > a { 1 , 2 , 3 , 4 , 5 };
for ( int x : a) x *= 2 ;
// a == { 1, 2, 3, 4, 5 }
사본 생성자 및 사본 할당 연산자는 사본이 만들어 질 때 호출되며 C ++ 11의 Move Semantics 소개를 통해 이동 생성자 및 이동 할당 연산자가 동작을 위해 있습니다.
struct A {
std::string s;
A () : s{ " test " } {}
A ( const A& o) : s{o. s } {}
A (A&& o) : s{ std::move (o. s )} {}
A& operator =(A&& o) {
s = std::move (o. s );
return * this ;
}
};
A f (A a) {
return a;
}
A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary
생성자를 변환하면 브레이스 목록 구문 값이 생성자 인수로 변환됩니다.
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
};
A a { 0 , 0 }; // calls A::A(int, int)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(int, int)
A d { 0 , 0 , 0 }; // calls A::A(int, int, int)
Braced List Syntax는 좁히는 것을 허용하지 않습니다.
struct A {
A ( int ) {}
};
A a ( 1.1 ); // OK
A b { 1.1 }; // Error narrowing conversion from double to int
생성자가 std::initializer_list
수락하면 대신 호출됩니다.
struct A {
A ( int ) {}
A ( int , int ) {}
A ( int , int , int ) {}
A (std::initializer_list< int >) {}
};
A a { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A b ( 0 , 0 ); // calls A::A(int, int)
A c = { 0 , 0 }; // calls A::A(std::initializer_list<int>)
A d { 0 , 0 , 0 }; // calls A::A(std::initializer_list<int>)
명시 explicit
지정자를 사용하여 변환 기능을 명시 적으로 만들 수 있습니다.
struct A {
operator bool () const { return true ; }
};
struct B {
explicit operator bool () const { return true ; }
};
A a;
if (a); // OK calls A::operator bool()
bool ba = a; // OK copy-initialization selects A::operator bool()
B b;
if (b); // OK calls B::operator bool()
bool bb = b; // error copy-initialization does not consider B::operator bool()
인라인 네임 스페이스의 모든 구성원은 부모 네임 스페이스의 일부인 것처럼 취급되어 기능을 전문화하고 버전 작성 프로세스를 완화 할 수 있습니다. 이것은 전이 속성이며, A가 B를 포함하는 경우 C와 B와 C가 모두 인라인 네임 스페이스 인 경우 C의 멤버는 마치 A에있는 것처럼 사용할 수 있습니다.
namespace Program {
namespace Version1 {
int getVersion () { return 1 ; }
bool isFirstVersion () { return true ; }
}
inline namespace Version2 {
int getVersion () { return 2 ; }
}
}
int version { Program::getVersion ()}; // Uses getVersion() from Version2
int oldVersion { Program::Version1::getVersion ()}; // Uses getVersion() from Version1
bool firstVersion { Program::isFirstVersion ()}; // Does not compile when Version2 is added
비 정적 데이터 멤버는 선언 된 곳에 초기화 될 수 있도록하여 기본 초기화의 생성자를 정리할 수 있습니다.
// Default initialization prior to C++11
class Human {
Human () : age{ 0 } {}
private:
unsigned age;
};
// Default initialization on C++11
class Human {
private:
unsigned age { 0 };
};
C ++ 11은 이제 일련의 직각 괄호가 연산자 또는 공백을 추가 할 필요없이 TypEdef의 폐쇄 명령문으로 사용될 때 추론 할 수 있습니다.
typedef std::map< int , std::map < int , std::map < int , int > > > cpp98LongTypedef;
typedef std::map< int , std::map < int , std::map < int , int >>> cpp11LongTypedef;
*회원 기능은 *this
LValue 또는 RValue Reference인지 여부에 따라 자격을 얻을 수 있습니다.
struct Bar {
// ...
};
struct Foo {
Bar& getBar () & { return bar; }
const Bar& getBar () const & { return bar; }
Bar&& getBar() && { return std::move (bar); }
const Bar&& getBar() const && { return std::move (bar); }
private:
Bar bar;
};
Foo foo{};
Bar bar = foo.getBar(); // calls `Bar& getBar() &`
const Foo foo2{};
Bar bar2 = foo2.getBar(); // calls `Bar& Foo::getBar() const&`
Foo{}.getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo).getBar(); // calls `Bar&& Foo::getBar() &&`
std::move (foo2).getBar(); // calls `const Bar&& Foo::getBar() const&`
C ++ 11을 사용하면 기능과 Lambdas가 반환 유형을 지정하기위한 대체 구문을 허용합니다.
int f () {
return 123 ;
}
// vs.
auto f () -> int {
return 123 ;
}
auto g = []() -> int {
return 123 ;
};
이 기능은 특정 반환 유형을 해결할 수없는 경우에 특히 유용합니다.
// NOTE: This does not compile!
template < typename T, typename U>
decltype (a + b) add(T a, U b) {
return a + b;
}
// Trailing return types allows this:
template < typename T, typename U>
auto add (T a, U b) -> decltype(a + b) {
return a + b;
}
C ++ 14에서 decltype(auto) (C++14)
대신 사용할 수 있습니다.
noexcept
지정자는 함수가 예외를 던질 수 있는지 여부를 지정합니다. throw()
의 개선 된 버전입니다.
void func1 () noexcept ; // does not throw
void func2 () noexcept ( true ); // does not throw
void func3 () throw(); // does not throw
void func4 () noexcept ( false ); // may throw
비 던지기 기능은 잠재적으로 던지는 기능을 호출 할 수 있습니다. 예외가 발생하고 핸들러 검색이 비 던지는 함수의 가장 바깥 쪽 블록을 만나면 std :: 종료 함수가 호출됩니다.
extern void f (); // potentially-throwing
void g () noexcept {
f (); // valid, even if f throws
throw 42 ; // valid, effectively a call to std::terminate
}
UTF-8 문자열을 나타내는 표준 유형을 제공합니다.
char32_t utf8_str[] = U" u0123 " ;
char16_t utf8_str[] = u" u0123 " ;
C ++ 11은 문자열 리터럴을 "원시 문자리 리터럴"으로 선언하는 새로운 방법을 소개합니다. 탈출 시퀀스 (탭, 선 피드, 단일 배송 등)에서 발행 된 문자는 서식을 보존하는 동안 RAW를 입력 할 수 있습니다. 예를 들어, 많은 인용문이나 특수 형식이 포함되어있을 수있는 문학 텍스트를 작성하는 데 유용합니다. 이렇게하면 문자열 리터럴을 쉽게 읽고 유지 관리 할 수 있습니다.
원시 문자열 문자는 다음 구문을 사용하여 선언됩니다.
R"delimiter(raw_characters)delimiter"
어디:
delimiter
괄호, 백 슬래시 및 공간을 제외한 모든 소스 문자로 만든 선택적 문자 순서입니다.raw_characters
원시 문자 순서입니다. 마감 시퀀스 ")delimiter"
포함해서는 안됩니다.예:
// msg1 and msg2 are equivalent.
const char * msg1 = " n Hello, nt world! n " ;
const char * msg2 = R"(
Hello,
world!
)" ;
std::move
전달 된 물체가 자원이 전달 될 수 있음을 나타냅니다. 지정되지 않은 상태로 남겨질 수 있으므로 이동 한 물체를 사용하여 조심스럽게 사용해야합니다 (참조 : 객체에서 이동할 수있는 일은 무엇입니까?).
std::move
의 정의 (움직임을 수행하는 것은 rvalue 참조에 캐스팅하는 것 이상) :
template < typename T>
typename remove_reference<T>::type&& move(T&& arg) {
return static_cast < typename remove_reference<T>::type&&>(arg);
}
std::unique_ptr
s 전송 :
std::unique_ptr< int > p1 { new int { 0 }}; // in practice, use std::make_unique
std::unique_ptr< int > p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr< int > p3 = std::move(p1); // move `p1` into `p3`
// now unsafe to dereference object held by `p1`
값 범주 및 CV Qualifiers를 유지하면서 전달 된 인수를 반환합니다. 일반 코드 및 공장에 유용합니다. forwarding references
와 함께 사용됩니다.
std::forward
의 정의 :
template < typename T>
T&& forward( typename remove_reference<T>::type& arg) {
return static_cast <T&&>(arg);
}
다른 A
를 새로운 A
의 사본으로 전달하거나 생성자를 이동하는 함수 wrapper
의 예 :
struct A {
A () = default ;
A ( const A& o) { std::cout << " copied " << std::endl; }
A (A&& o) { std::cout << " moved " << std::endl; }
};
template < typename T>
A wrapper (T&& arg) {
return A{std::forward<T>(arg)};
}
wrapper (A{}); // moved
A a;
wrapper (a); // copied
wrapper (std::move(a)); // moved
forwarding references
, rvalue references
도 참조하십시오.
std::thread
라이브러리는 스레드를 산란 및 죽이는 것과 같은 스레드를 제어하는 표준 방법을 제공합니다. 아래의 예에서, 여러 스레드가 다른 계산을 수행하기 위해 스폰 된 다음 프로그램이 모두 완료되기를 기다립니다.
void foo ( bool clause) { /* do something... */ }
std::vector<std::thread> threadsVector;
threadsVector.emplace_back([]() {
// Lambda function that will be invoked
});
threadsVector.emplace_back(foo, true ); // thread will run foo(true)
for ( auto & thread : threadsVector) {
thread. join (); // Wait for threads to finish
}
숫자 인수를 std::string
로 변환합니다.
std::to_string ( 1.2 ); // == "1.2"
std::to_string ( 123 ); // == "123"
유형 특성은 컴파일 타임 템플릿 기반 인터페이스를 정의하여 유형의 속성을 쿼리하거나 수정합니다.
static_assert (std::is_integral< int >::value);
static_assert (std::is_same< int , int >::value);
static_assert (std::is_same<std::conditional< true , int , double >::type, int >::value);
C ++ 11은 새로운 스마트 포인터를 소개합니다 : std::unique_ptr
, std::shared_ptr
, std::weak_ptr
. std::auto_ptr
이제 더 이상 사용되지 않고 결국 C ++ 17에서 제거되었습니다.
std::unique_ptr
은 자체 힙 할당 메모리를 관리하는 인사가 불가능하고 움직일 수있는 포인터입니다. 참고 : std::make_X
헬퍼 기능을 사용하여 생성자 사용과 달리 사용하는 것이 좋습니다. std :: make_unique 및 std :: make_shared의 섹션을 참조하십시오.
std::unique_ptr<Foo> p1 { new Foo{} }; // `p1` owns `Foo`
if (p1) {
p1-> bar ();
}
{
std::unique_ptr<Foo> p2 { std::move (p1)}; // Now `p2` owns `Foo`
f (*p2);
p1 = std::move (p2); // Ownership returns to `p1` -- `p2` gets destroyed
}
if (p1) {
p1-> bar ();
}
// `Foo` instance is destroyed when `p1` goes out of scope
std::shared_ptr
는 여러 소유자에서 공유되는 리소스를 관리하는 스마트 포인터입니다. 공유 포인터는 관리 대상 및 참조 카운터와 같은 몇 가지 구성 요소를 갖는 제어 블록을 보유합니다. 모든 제어 블록 액세스는 스레드 안전이지만 관리되는 객체 자체를 조작하는 것은 스레드 안전이 아닙니다 .
void foo (std::shared_ptr<T> t) {
// Do something with `t`...
}
void bar (std::shared_ptr<T> t) {
// Do something with `t`...
}
void baz (std::shared_ptr<T> t) {
// Do something with `t`...
}
std::shared_ptr<T> p1 { new T{}};
// Perhaps these take place in another threads?
foo (p1);
bar (p1);
baz (p1);
Chrono 라이브러리에는 기간 , 시계 및 시점을 다루는 일련의 유틸리티 기능 및 유형이 포함되어 있습니다. 이 라이브러리의 사용 사례 중 하나는 벤치마킹 코드입니다.
std::chrono::time_point<std::chrono::steady_clock> start, end;
start = std::chrono::steady_clock::now();
// Some computations...
end = std::chrono::steady_clock::now();
std::chrono::duration< double > elapsed_seconds = end - start;
double t = elapsed_seconds.count(); // t number of seconds, represented as a `double`
튜플은 이종 값의 고정 크기 컬렉션입니다. std::tie
사용하여 포장을 풀거나 std::get
사용하여 std::tuple
의 요소에 액세스하십시오.
// `playerProfile` has type `std::tuple<int, const char*, const char*>`.
auto playerProfile = std::make_tuple( 51 , " Frans Nielsen " , " NYI " );
std::get< 0 >(playerProfile); // 51
std::get< 1 >(playerProfile); // "Frans Nielsen"
std::get< 2 >(playerProfile); // "NYI"
lvalue 참조의 튜플을 만듭니다. std::pair
및 std::tuple
물체를 풀기 위해 유용합니다. std::ignore
. C ++ 17에서는 대신 구조화 된 바인딩을 사용해야합니다.
// With tuples...
std::string playerName;
std::tie (std::ignore, playerName, std::ignore) = std::make_tuple( 91 , " John Tavares " , " NYI " );
// With pairs...
std::string yes, no;
std::tie (yes, no) = std::make_pair( " yes " , " no " );
std::array
C 스타일 어레이 위에 내장 된 컨테이너입니다. 정렬과 같은 일반적인 컨테이너 작업을 지원합니다.
std::array< int , 3 > a = { 2 , 1 , 3 };
std::sort (a.begin(), a.end()); // a == { 1, 2, 3 }
for ( int & x : a) x *= 2 ; // a == { 2, 4, 6 }
이 컨테이너는 검색, 삽입 및 제거에 대한 평균 일정한 시간 복잡성을 유지합니다. 일정한 시간의 복잡성을 달성하기 위해, 버킷에 요소를 해시하여 속도를 희생합니다. 4 개의 변수되지 않은 컨테이너가 있습니다.
unordered_set
unordered_multiset
unordered_map
unordered_multimap
std::make_shared
다음과 같은 이유로 std::shared_ptr
s의 인스턴스를 생성하는 것이 권장되는 방법입니다.
new
연산자를 사용하지 않아도됩니다.foo
와 같은 기능을 부르고 있다고 가정 해 봅시다. foo (std::shared_ptr<T>{ new T{}}, function_that_throws(), std::shared_ptr<T>{ new T{}});
컴파일러는 new T{}
자유롭게 호출 한 다음 function_that_throws()
등을 호출 할 수 있습니다 ... T
의 첫 번째 구성에서 힙에 데이터를 할당했기 때문에 여기에 누출이 도입되었습니다. std::make_shared
사용하면 예외 안전성이 주어집니다.
foo (std::make_shared<T>(), function_that_throws(), std::make_shared<T>());
std::shared_ptr{ new T{} }
호출 할 때, 우리는 T
에 대한 메모리를 할당 한 다음, 공유 포인터에서는 포인터 내의 제어 블록에 대한 메모리를 할당해야합니다. std::unique_ptr
및 std::shared_ptr
에 대한 자세한 내용은 스마트 포인터 섹션을 참조하십시오.
std::ref(val)
val의 참조를 보유한 std::reference_wrapper
유형의 객체를 만드는 데 사용됩니다. 유형 공제로 인해 일반적인 참조 통과 &
&
을 사용하지 않거나 삭제되는 경우에 사용됩니다. std::cref
비슷하지만 생성 된 참조 래퍼는 Val에 대한 Const 참조를 보유하고 있습니다.
// create a container to store reference of objects.
auto val = 99 ;
auto _ref = std::ref(val);
_ref++;
auto _cref = std::cref(val);
// _cref++; does not compile
std::vector<std::reference_wrapper< int >>vec; // vector<int&>vec does not compile
vec.push_back(_ref); // vec.push_back(&i) does not compile
cout << val << endl; // prints 100
cout << vec[ 0 ] << endl; // prints 100
cout << _cref; // prints 100
C ++ 11은 C ++에 대한 메모리 모델을 소개합니다. 이는 스레딩 및 원자 연산에 대한 라이브러리 지원을 의미합니다. 이러한 작업 중 일부에는 원자 부하/상점, 비교 및 스웨이, 원자 플래그, 약속, 미래, 자물쇠 및 조건 변수가 포함됩니다.
다음 섹션을 참조하십시오
std::async
주어진 함수를 비동기 적으로 또는 게으른 평가 한 다음 해당 함수 호출의 결과를 보유하는 std::future
를 반환합니다.
첫 번째 매개 변수는 다음과 같은 정책입니다.
std::launch::async | std::launch::deferred
구현에 달려 있습니다.std::launch::async
새 스레드에서 호출 가능한 객체를 실행합니다.std::launch::deferred
현재 스레드에서 게으른 평가를 수행합니다. int foo () {
/* Do something here, then return the result. */
return 1000 ;
}
auto handle = std::async(std::launch::async, foo); // create an async task
auto result = handle.get(); // wait for the result
std::begin
및 std::end
free functions가 추가되어 컨테이너의 시작 및 종료 반복자를 일반적으로 반환했습니다. 이 기능은 또한 멤버 기능을 begin
하고 end
하지 않는 원시 어레이에서도 작동합니다.
template < typename T>
int CountTwos ( const T& container) {
return std::count_if ( std::begin (container), std::end (container), []( int item) {
return item == 2 ;
});
}
std::vector< int > vec = { 2 , 2 , 43 , 435 , 4543 , 534 };
int arr[ 8 ] = { 2 , 43 , 45 , 435 , 32 , 32 , 32 , 32 };
auto a = CountTwos(vec); // 2
auto b = CountTwos(arr); // 1
Anthony Calandra
https://github.com/anthonycalandra/modern-cpp-features/graphs/contributors를 참조하십시오
MIT