나무모에 미러 (일반/밝은 화면)
최근 수정 시각 : 2024-09-11 15:12:01

C++/문법/특성

파일:관련 문서 아이콘.svg   관련 문서: C++
,
,
,
,
,

파일:상위 문서 아이콘.svg   상위 문서: C++/문법
프로그래밍 언어 문법
{{{#!wiki style="margin: -16px -11px; word-break: keep-all"<colbgcolor=#0095c7><colcolor=#fff,#000> 언어 문법 C(포인터 · 구조체 · size_t) · C++(자료형 · 클래스 · 이름공간 · 상수 표현식 · 특성) · C# · Java · Python(함수 · 모듈) · Kotlin · MATLAB · SQL · PHP · JavaScript · Haskell(모나드)
마크업 문법 HTML · CSS
개념과 용어 함수(인라인 함수 · 고차 함수 · 람다식) · 리터럴 · 상속 · 예외 · 조건문 · 참조에 의한 호출 · eval
기타 == · === · deprecated · NaN · null · undefined · 배커스-나우르 표기법
프로그래밍 언어 예제 · 목록 · 분류 }}}

1. 개요2. 용도3. 표준 특성 목록
3.1. nodiscard3.2. maybe_unused3.3. noreturn3.4. deprecated3.5. no_unique_address
4. 외부 컴파일러의 특성 목록
4.1. Clang
4.1.1. lifetimebound4.1.2. internal_linkage4.1.3. tls_model
4.2. MSVC
4.2.1. no_unique_address4.2.2. lifetimebound

1. 개요

C++11부터 추가된 C++의 특성(Attribute)에 대하여 설명하는 문서.

2. 용도

예전의 C 때부터 표준 언어 명세에 기능을 덧붙이기 위해 여러 컴파일러에서 다양한 기능을 구현해왔다. 보통은 표준을 해치지 않기 위해 함수처럼 못생긴 키워드와 함께 기능의 이름을 전달하는 식으로 사용했다. 가령 Clang__attribute__(name)MSVC__declspec(name) 따위가 있다. C++11에서는 이 중구난방한 키워드를 통일하고 C++의 외양과 방식에 걸맞은 표준을 제정한 것이 바로 특성이다.

특성은 컴파일러에게 C++의 객체 [1]에 대해 결정론적인 동작을 수행하도록 명령을 내린다. C++의 특성은 Java, Python의 장식자(Decorator)나 C#의 특성(Attribute)과 쓰는 방식은 거의 같지만 용도는 조금 다르다. C++의 특성은 사용자가 정의할 수 없고, 컴파일 시점에 동작이 결정된다.

3. 표준 특성 목록

<rowcolor=#d7d7d7,#a1a1a1>이름용법설명
carries_dependency[[carries_dependency]] C++11 원자적 메모리 모델에서 std::memory_order_consume을 사용할 때, 굳이 매개변수에 읽기 쓰기 순서 유지를 사용하지 않아도 되는 경우를 알린다. 즉 함수의 매개변수가 std::atomic에서 값을 읽더라도 성능 저하를 줄일 수 있다.
noreturn[[noreturn]] C++11 [후술]
deprecated[[deprecated]] C++14 [후술]
[[deprecated("message")]] C++17
fallthrough[[fallthrough]] C++17 switch문에서 각 case가 끊기지 않고 실행됨을 알린다.
maybe_unused[[maybe_unused]] C++17 [후술]
nodiscard[[nodiscard]] C++17 [후술]
[[nodiscard("message")]] C++20
likely[[likely]] C++20 CPU가 분기 예측을 수행할 때 해당 함수를 프리패치의 선호 대상에 넣도록 지시한다.
no_unique_address[[no_unique_address]] C++20 [후술]
unlikely[[unlikely]] C++20 CPU가 분기 예측을 수행할 때 해당 함수를 프리패치의 선호 대상에 넣지 말도록 지시한다. 항상 파이프라인 실속이 발생한다.
assume[[assume(expr)]] C++23 구문 expr를 평가한 결과가 참(true)이 아니면 이후의 코드를 도달할 수 없는(unreachable) 코드로 평가한다. 특이하게 expr는 상수 평가식이 아니여도 된다.

3.1. nodiscard

#!syntax cpp
import <memory>;

[[nodiscard]]
consteval int FiveHundred() noexcept 
{
    return 500;
}

struct Conscience {};

template<typename T>
[[nodiscard("여기 양심 떨어트리셨습니다")]]
constexpr Conscience MyConscience()
{
    return Conscience{};
}

static inline constexpr const char* msg = "Take smart pointer!";

template<typename T>
[[nodiscard(msg)]]
constexpr std::unique_ptr<T> MakeThing()
{
    return std::make_unique<T>();
}

enum [[nodiscard("일단 뽑았으면 무라도 썰어야")]] Sword : long long {};

class [[nodiscard("노상방뇨는 안됩니다.")]] Pee {};

// 사용하지 않으면 경고가 뜬다.
int main()
{
    FiveHundred();
    MyConscience();
    MakeThing<long>();
    Sword{};
    new Pee;

    return 0;
}

[[nodiscard]]
함수의 반환값을 무시하지 못하게 경고를 띄우는 특성. 함수의 템플릿 정의와 지시자 사이에 추가할 수 있다. 반환 값을 날리면 치명적인 함수에 달아주면 좋다. 예를 들어 C++의 메모리 관리가 까다롭기 때문에 메모리 할당 함수에 달면 좋다. 참고로 표준 라이브러리의 경우 이미 오만가지 함수에 죄다 달려있다.

3.2. maybe_unused

#!syntax cpp
// C 표준 main 함수
int main([[maybe_unused]] const int& argc, [[maybe_unused]] const char* const* argv)
{
    // argc, argv를 사용하지 않아도 경고가 발생하지 않는다.

    [[maybe_unused]] int unuse0{};
    [[maybe_unused]] float unused1;
    // unuse0, unused1을 사용하지 않아도 경고가 발생하지 않는다.

    return 0;
}
[[maybe_unused]]
변수 미사용 경고를 해제하는 특성. 원래는 UNREFERENCED_PARAM(var)같은 매크로를 써서 경고를 막곤 했으나 이 특성을 쓰면 매크로를 쓸 필요가 없다.

3.3. noreturn

#!syntax cpp
[[noreturn]]
static void _ThrowPopError()
{
    throw "Pop!";
}

[[noreturn]] void unreachable() {}

int main()
{
    // 실행 시간에 예외 발생
    _ThrowPopError();

    // 컴파일 시간에 경고 발생
    auto somewhat_peaceful_moment = "Whassup";

    return 0;
}
[[noreturn]]
함수의 실행 기점 이후를 도달할 수 없는 코드로 처리하는 특성. 해당 함수가 실행되면 이후 코드에 경고가 발생한다.

3.4. deprecated

#!syntax cpp
[[deprecated("제발 한국인이라면 맨유 좀 응원합시다")]]
std::string WatchMU() noexcept;

class [[deprecated("Please don't use this")]] MFC
{
public:
    [[deprecated]] const char* version = "Windows XP";
};

/// @see {@link "https://gcemetery.co/"}
enum class GoogleServices
{
    Allo [[deprecated]],
    Android,
    Fabric [[deprecated]],
    GoogleClips [[deprecated]],
    GoogleGlass [[deprecated]],
    GooglePlus [[deprecated]],
    Hangouts [[deprecated]],
    Nexus [[deprecated]],
    Pixel,
    Youtube,
};
[[deprecated]], [[deprecated(msg)]] [파면]
해당 객체나 이름공간을 사용하지 말도록 권고하는 특성. 해당 객체가 사용되면 경고가 발생한다. 이를 응용하려면 전처리기 키워드로 감싸서 선택적 기능으로 제공해야 한다.

3.5. no_unique_address

#!syntax cpp
// sizeof(DeclarationOnly) == 0
class DeclarationOnly;

// sizeof(Trait<T>) == 1
template<typename T>
struct Trait
{
    using value_type = T;
    using const_type = const T;
    using refernce = T&;
    using const_refernce = const T&;
};

// sizeof(CompleteClass<T>) == sizeof(T) * 10
template<typename T>
class CompleteClass
{
public: // std::addressof(myData[0]) == std::addressof(mySettings)
    [[no_unique_address]] Trait<T> mySettings;
    T[10] myData;
};

[[no_unique_address]]
클래스, 구조체의 필드에 주소를 가지지 않도록 지시하는 특성. 이 특성이 사용된 필드는 다른 필드와 겹쳐질 수 있다. C++에는 자료형 별칭만 갖고 있거나, 아무런 비정적 필드가 없는 완성된 클래스는 바이트 크기가 1로 최적화된다. 그러나 바이트 크기가 1인 이유는 단순히 선언만 있는 클래스의 크기가 0이므로 이를 구분하기 위함이다. 때문에 크기 1조차 공간 낭비라면 이 특성을 사용해 바이트를 절약할 수 있다.

4. 외부 컴파일러의 특성 목록

4.1. Clang

4.1.1. lifetimebound

[[clang::lifetimebound]]
참조 대상 소실(Dangling)을 미리 경고하기 위한 특성.

4.1.2. internal_linkage

[[clang::internal_linkage]] #
C++에서 Cstatic의 특징을 구현하기 위한 특성. 마찬가지로 전역 이름공간에 정의된 객체의 사용 범위를 현재 파일 또는 현재 코드 범위(Scope) 내부로 한정짓는다. 즉, class NaumWiki;처럼 이름만 있는 클래스, void Print();처럼 선언만 있는 함수, static int myCount; 처럼 선언만 있는 변수와 같이 구현 내용이 없는 객체의 사용을 막는다. 구현 부분이 없으면 링크 오류를 발생시킨다. 이 특성을 쓰면 C++20의 모듈에서 export를 사용할 수 없으므로 주의해야 한다. 왜냐하면 exportstatic의 반대되는 키워드인 extern[8]에 더해서 선택적으로 객체를 외부로 노출시키기 때문이다.

4.1.3. tls_model

[[clang::tls_model("종류")]] #
Visual Studiothread_local과 호환되는 메모리를 구현하는 특성. 똑같이 변수의 이름 앞에 붙여 사용하면 된다.

4.2. MSVC

4.2.1. no_unique_address

[[msvc::no_unique_address]]
표준 라이브러리의 no_unique_address를 여기다가 구현 해놨다.

4.2.2. lifetimebound

[[msvc::lifetimebound]]
Clang의 lifetimebound와 대동소이하다.


[1] 완성된 클래스, 참조가 아닌 필드, 포인터, 함수[후술] [후술] [후술] [후술] [후술] [파면] [8] extern은 이 키워드는 반대로 선언만 있어도 사용할 수 있게 해준다. C++의 경우 모든 객체의 선언과 구현부에 보이지 않는 extern "C++"이 붙어있으므로 신경쓸 필요는 없다.