5 분 소요

개요

  • 타입, 객체, 코드 등에 대한 구현/정의 속성을 설명
  • 문법
    • C++11
      • [[ attribute-list ]]
    • C++17
      • [[ using attribute-namespace : attribute-list ]]
  • 표준 속성
    • [[noreturn]]
      • C++11
      • 함수가 return 하지 않음을 지정
    • 함수가 반환되는 경우 동작이 정의되지 않음
    • 함수 선언에서 함수 이름에만 적용 가능
    • throw 사용하여 함수를 빠져나가야 함
    • [[carries_dependency]]
      • C++11
      • indicates that dependency chain in release-consume std::memory_order propagates in and out of the function
    • [[deprecated]] / [[deprecated(“reason”)]]
      • C++14
      • 사용은 허용되지만 권장되지 않음
    • 사용 시 컴파일 경고 발생
    • class/struct/union
      • class [[deprecated]] C;
    • typedef/using
      • [[deprecated]] typedef C* PC;
      • using PC [[deprecated]] = C*;
    • variable
      • [[deprecated]] int i;
    • non-static 데이터 멤버
      • class T { [[deprecated]] int i; };
    • 함수
      • [[deprecated]] void f();
    • namespace
      • namespace [[deprecated]] NS { int i; }
    • enum
      • enum [[deprecated]] E {};
      • enum { A [[deprecated]], B [[deprecated]] = 42 };
        • C++17
    • 템플릿
      • template <> class [[deprecated]] C<int> {};
    • [[fallthrough]]
      • C++17
    • switch문에서 특정 라벨에 break가 없는게 의도적임을 지정
    • 컴파일 경고가 발생하지 않음
    • [[nodiscard]] / [[nodiscard(“reason”)]]
      • [[nodiscard]]
      • C++17
    • [[nodiscard(“reason”)]]
      • C++20
    • 함수의 반환 값을 사용하지 않으면 컴파일 경고 발생
    • 해당 속성으로 선언된 함수
    • 해당 속성으로 선언된 enum, class 등을 반환하는 함수
    • [[maybe_unused]]
      • C++17
    • 사용하지 않는 변수 등에 대한 경고를 억제
    • [[likely]] / [[unlikely]]
      • C++20
      • 컴파일러가 적절한 최적화를 하도록 분기문에 실행 빈도를 지정
    • [[no_unique_address]]
      • C++20
      • non-static 데이터 멤버가 다른 non-static 데이터 멤버와 구별되는 주소를 가질 필요가 없음을 지정
    • [[assume]]
      • C++23
    • 표현식이 항상 true로 평가되도록 지정


예제

  • [[noreturn]]
    • 코드
        #include <iostream>

        using namespace std;

        [[noreturn]] void func1() { throw "throw"; };

        void func2 [[noreturn]] () { throw "throw"; };

        int main() {
        	try {
        		func1();
        	} catch (...) {
        		cout << "~~~ func1()" << endl;
        	}

        	try {
        		func2();
        	} catch (...) {
        		cout << "~~~ func2()" << endl;
        	}

        	return 0;
        }
  • 실행 결과
        ~~~ func1()
        ~~~ func2()
  • [[deprecated]] / [[deprecated(“reason”)]]
    • 코드
        #include <iostream>
        #include <string>

        using namespace std;

        class [[deprecated]] Test1 {
        	public:
        		void Print() { cout << "Test1::Print()" << endl; }
        };

        class Test2 {
        	public:
        		[[deprecated]] int i;
        		[[deprecated]] void Print() { cout << "Test2::Print()" << endl; }
        };

        template <typename T> class Test3 {};
        template <> class [[deprecated]] Test3<int> {};

        [[deprecated]] int I = 0;

        int main() {
        	cout << I << endl;

        	Test1().Print();

        	Test2().i;
        	Test2().Print();

        	[[deprecated]] int i = 0;
        	cout << i << endl;

        	Test3<string>();
        	Test3<int>();

        	return 0;
        }
  • 실행 결과
        main.cpp: In function ‘int main()’:
        main.cpp:23:17: warning: ‘I’ is deprecated [-Wdeprecated-declarations]
           23 |         cout << I << endl;
              |                 ^
        main.cpp:20:20: note: declared here
           20 | [[deprecated]] int I = 0;
              |                    ^
        main.cpp:25:15: warning: ‘Test1’ is deprecated [-Wdeprecated-declarations]
           25 |         Test1().Print();
              |               ^
        main.cpp:6:22: note: declared here
            6 | class [[deprecated]] Test1 {
              |                      ^~~~~
        main.cpp:27:17: warning: ‘Test2::i’ is deprecated [-Wdeprecated-declarations]
           27 |         Test2().i;
              |                 ^
        main.cpp:13:36: note: declared here
           13 |                 [[deprecated]] int i;
              |                                    ^
        main.cpp:27:17: warning: ‘Test2::i’ is deprecated [-Wdeprecated-declarations]
           27 |         Test2().i;
              |                 ^
        main.cpp:13:36: note: declared here
           13 |                 [[deprecated]] int i;
              |                                    ^
        main.cpp:27:17: warning: ‘Test2::i’ is deprecated [-Wdeprecated-declarations]
           27 |         Test2().i;
              |                 ^
        main.cpp:13:36: note: declared here
           13 |                 [[deprecated]] int i;
              |                                    ^
        main.cpp:28:22: warning: ‘void Test2::Print()’ is deprecated [-Wdeprecated-declarations]
           28 |         Test2().Print();
              |         ~~~~~~~~~~~~~^~
        main.cpp:14:37: note: declared here
           14 |                 [[deprecated]] void Print() { cout << "Test2::Print()" << endl; }
              |                                     ^~~~~
        main.cpp:31:17: warning: ‘i’ is deprecated [-Wdeprecated-declarations]
           31 |         cout << i << endl;
              |                 ^
        main.cpp:30:28: note: declared here
           30 |         [[deprecated]] int i = 0;
              |                            ^
        main.cpp:34:20: warning: ‘Test3’ is deprecated [-Wdeprecated-declarations]
           34 |         Test3<int>();
              |                    ^
        main.cpp:18:34: note: declared here
           18 | template <> class [[deprecated]] Test3<int> {};
              |                                  ^~~~~~~~~~
        0
        Test1::Print()
        Test2::Print()
        0
  • [[fallthrough]]
    • 코드
        #include <iostream>

        using namespace std;

        int main() {
        	int i = 0;

        	switch (i) {
        	case 0:
        		cout << 0 << endl;
        		[[fallthrough]];
        	case 1:
        		cout << 1 << endl;
        		break;
        	}

        	return 0;
        }
  • 실행 결과
        0
        1
  • [[nodiscard]] / [[nodiscard(“reason”)]]
    • 코드
        class [[nodiscard]] Test {};
        Test func1() { return Test(); }

        [[nodiscard("reason")]] int func2() { return 1; };

        int main() {
        	func1();

        	func2();

        	return 0;
        }
  • 실행 결과
        main.cpp: In function ‘int main()’:
        main.cpp:7:14: warning: ignoring returned value of type ‘Test’, declared with attribute ‘nodiscard’ [-Wunused-result]
            7 |         func1();
              |         ~~~~~^~
        main.cpp:2:6: note: in call to ‘Test func1()’, declared here
            2 | Test func1() { return Test(); }
              |      ^~~~~
        main.cpp:1:21: note: ‘Test’ declared here
            1 | class [[nodiscard]] Test {};
              |                     ^~~~
        main.cpp:9:14: warning: ignoring return value of ‘int func2()’, declared with attribute ‘nodiscard’: ‘reason’ [-Wunused-result]
            9 |         func2();
              |         ~~~~~^~
        main.cpp:4:29: note: declared here
            4 | [[nodiscard("reason")]] int func2() { return 1; };
              |                             ^~~~~
  • [[maybe_unused]]
    • 코드
        [[maybe_unused]] void func([[maybe_unused]] int x, int y) {}

        int main() {
        	int i = 0;
        	[[maybe_unused]] double d = 0.0;

        	return 0;
        }
  • 실행 결과
        main.cpp: In function ‘int main()’:
        main.cpp:4:13: warning: unused variable ‘i’ [-Wunused-variable]
            4 |         int i = 0;
              |             ^
  • [[likely]] / [[unlikely]]
    • 코드
        #include <algorithm>
        #include <chrono>
        #include <iostream>

        using namespace std;

        constexpr long long fact_with_attributes(long long n) noexcept {
        	if (n > 1) [[likely]] {
        		return n * fact_with_attributes(n - 1);
        	} else [[unlikely]] {
        		return 1;
        	}
        }

        constexpr long long fact_no_attributes(long long n) noexcept {
        	if (n > 1) {
        		return n * fact_no_attributes(n - 1);
        	} else {
        		return 1;
        	}
        }

        int main() {
        	auto run = [](auto func) {
        		const auto start = chrono::high_resolution_clock::now();

        		for (int i = 0; i < 2000000; i++) {
        			for (int j = 0; j <= 20; j++) {
        				func(j);
        			}
        		}

        		const auto end = chrono::high_resolution_clock::now();

        		const chrono::duration<double> result = end - start;
        		cout << result.count() << endl;
        	};

        	run(fact_with_attributes);
        	run(fact_no_attributes);

        	return 0;
        }
  • 실행 결과
        1.68545
        1.7237
  • [[no_unique_address]]
    • 코드
        #include <iostream>

        using namespace std;

        class Test {};

        class Test1 {
        	private:
        		int i;
        		Test t;

        	public:
        		void Print() {
        			cout << &this->i << endl;
        			cout << &this->t << endl;
        		}
        };

        class Test2 {
        	private:
        		int i;
        		[[no_unique_address]] Test t;

        	public:
        		void Print() {
        			cout << &this->i << endl;
        			cout << &this->t << endl;
        		}
        };

        int main() {
        	cout << sizeof(int) << endl;
        	cout << sizeof(Test) << endl;
        	cout << sizeof(Test1) << endl;
        	cout << sizeof(Test2) << endl;

        	cout << "------" << endl;

        	Test1().Print();

        	cout << "------" << endl;

        	Test2().Print();

        	return 0;
        }
  • 실행 결과
        4
        1
        8
        4
        ------
        0x7ffd4e7a40b4
        0x7ffd4e7a40b8
        ------
        0x7ffd4e7a40bc
        0x7ffd4e7a40bc