속성(Attributes)
속성(Attributes)
컴파일러마다 가진 확장 기능을 표준적인 문법 하나로 통일해 쓰고 싶을 때가 있어요. C++11부터 도입된 **속성(attributes)**이 바로 그 역할을 해요. 속성은 타입, 객체, 코드 등에 대한 구현 정의(implementation-defined) 속성을 도입하는 표준 문법이에요.
출처: cppreference
본문
문법
[[ attribute-list ]] |
(C++11부터) | |
[[ using attribute-namespace : attribute-list ]] |
(C++17부터) |
여기서 attribute-list는 0개 이상의 속성들의 쉼표로 구분된 시퀀스예요(팩 확장을 나타내는 줄임표 ...로 끝날 수 있어요).
| 식별자 | (1) | |
attribute-namespace :: 식별자 |
(2) | |
식별자 ( argument-list(optional) ) |
(3) | |
attribute-namespace :: 식별자 ( argument-list(optional) ) |
(4) |
여기서 attribute-namespace는 식별자이고, argument-list는 괄호·대괄호·중괄호가 균형 잡힌 토큰 시퀀스(balanced-token-seq)예요.
속성 목록의 시작에 using namespace:가 나오면, 속성 목록의 다른 속성들은 이름공간을 지정할 수 없어요. using에 지정된 이름공간이 그 모두에 적용되거든요. (C++17부터)
[[using CC: opt(1), debug]] // [[CC::opt(1), CC::debug]]와 같음
[[using CC: CC::opt(1)]] // error: using과 스코프 속성을 결합할 수 없음
속성 지정자의 모든 항목이 =로 시작하면, 그 속성 지정자는 대신 **주석(annotations)**을 도입해요. =로 시작하지 않는 속성과 =로 시작하는 주석은 같은 속성 지정자 안에서 섞일 수 없어요. (C++26부터)
동작 방식(Explanation)
속성은 GNU와 IBM의 언어 확장 __attribute__((...)), Microsoft 확장 __declspec() 같은 구현 정의 언어 확장을 위한 통일된 표준 문법을 제공해요.
속성은 C++ 프로그램의 거의 어디에나 쓸 수 있고, 타입, 변수, 함수, 이름, 코드 블록, 전체 번역 단위 등 거의 모든 것에 적용할 수 있어요. 다만 각 개별 속성은 구현이 허용하는 자리에서만 유효해요. [[expect_true]]는 if에만 쓸 수 있고 클래스 선언에는 쓸 수 없는 속성일 수 있고, [[omp::parallel()]]은 코드 블록이나 for 루프에 적용되지만 타입 int에는 적용되지 않는 속성일 수 있어요(이 두 속성은 가상의 예시예요. 표준 및 일부 비표준 속성은 아래 참고).
선언에서 속성은 선언 전체 앞과, 선언되는 실체의 이름 바로 뒤에 모두 나타날 수 있어요. 그 경우 결합돼요. 대부분의 다른 상황에서 속성은 바로 앞의 실체에 적용돼요.
alignas 지정자는 문법이 다르지만 속성 지정자 시퀀스의 일부예요. [[...]] 속성이 나타날 수 있는 자리에 나타날 수 있고, (허용되는 자리라면) 섞어 쓸 수도 있어요.
연속된 두 개의 왼쪽 대괄호 토큰([[)은 속성 지정자를 도입할 때나 속성 인자 안에서만 나타날 수 있어요.
void f()
{
int y[3];
y[[] { return 0; }()] = 1; // error
int i [[cats::meow([[]])]]; // OK
}
아래 나열된 표준 속성들 외에도, 구현은 구현 정의 동작을 가진 임의의 비표준 속성을 지원할 수 있어요. 구현이 모르는 모든 속성은 오류 없이 무시돼요. (C++17부터)
attribute-namespace가 없는 속성, 그리고 이름이 std이거나 std 뒤에 숫자가 하나 이상 붙은 attribute-namespace는 향후 표준화를 위해 예약돼 있어요. 즉, 모든 비표준 속성은 구현이 제공하는 attribute-namespace 안에 있어요. 예를 들어 [[gnu::may_alias]], [[clang::trivial_abi]], [[msvc::noop_dtor]] 같은 것들이에요. (C++20부터)
표준 속성(Standard attributes)
다음 속성들이 C++ 표준으로 정의돼 있어요.
표준 속성은 문법적으로 무시될 수 없어요. 즉, 문법 오류를 담을 수 없고, 올바른 대상에 적용돼야 하며, 인자의 실체는 odr-사용돼야 해요.
표준 속성은 의미상으로도 무시될 수 없어요. 특정 표준 속성의 모든 인스턴스를 제거한 동작이, 그 속성이 있는 원래 프로그램에 대한 적합한 동작(conforming behavior)이었을 것이기 때문이에요.
| 속성 | 의미 |
|---|---|
[[noreturn]](C++11) |
함수가 반환하지 않음을 나타냄 |
[[carries_dependency]](C++11, C++26에서 제거) |
release-consume std::memory_order의 의존성 체인이 함수 안팎으로 전파됨을 나타냄 |
[[deprecated]] / [[deprecated("이유")]](C++14) |
이 속성으로 선언된 이름이나 실체의 사용이 허용되지만, 어떤 이유로 권장되지 않음을 나타냄 |
[[fallthrough]](C++17) |
이전 case 레이블에서의 낙하(fall through)가 의도적임을 나타내고, fall-through에 경고하는 컴파일러가 진단하지 않도록 함 |
[[maybe_unused]](C++17) |
사용되지 않는 실체에 대한 컴파일러 경고를 (있는 경우) 억제함 |
[[nodiscard]] / [[nodiscard("이유")]](C++17, C++20) |
반환 값이 버려지면 컴파일러가 경고를 내도록 권장함 |
[[likely]] / [[unlikely]](C++20) |
문을 통과하는 실행 경로가 다른 경로보다 더(또는 덜) 가능성 있다고 생각하고 컴파일러가 최적화하도록 나타냄 |
[[no_unique_address]](C++20) |
비정적 데이터 멤버가 자기 클래스의 다른 모든 비정적 데이터 멤버와 구별되는 주소를 가질 필요가 없음을 나타냄 |
[[assume(표현식)]](C++23) |
주어진 지점에서 그 표현식이 항상 true로 평가될 것임을 명시함 |
[[indeterminate]](C++26) |
초기화되지 않은 객체가 불확정 값(indeterminate value)을 가짐을 명시함 |
[[optimize_for_synchronized]](TM TS) |
함수 정의가 synchronized 문에서의 호출에 최적화되어야 함을 나타냄 |
주의할 점(Notes)
각 개별 속성의 특정 플랫폼에서의 존재 여부는 __has_cpp_attribute 전처리기 매크로로 확인할 수 있어요.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_attributes |
200809L | (C++11) | Attributes |
__cpp_namespace_attributes |
201411L | (C++17) | 이름공간용 속성 |
예제
같은 속성들을 어떻게 여러 방식으로 선언할 수 있는지 볼게요.
[[gnu::always_inline]] [[gnu::hot]] [[gnu::const]] [[nodiscard]]
inline int f(); // 네 개의 속성으로 f 선언
[[gnu::always_inline, gnu::const, gnu::hot, nodiscard]]
int f(); // 위와 같지만, 네 개의 속성을 담은 단일 속성 지정자를 사용
// C++17:
[[using gnu : const, always_inline, hot]] [[nodiscard]]
int f[[gnu::always_inline]](); // 속성은 여러 지정자에 나타날 수 있음
int f() { return 0; }
int main() {}
결함 보고(Defect reports)
이전에 발표된 C++ 표준들에 소급 적용된 동작 변경 결함 보고들이 있어요.
| DR | 적용 대상 | 발표 당시 동작 | 올바른 동작 |
|---|---|---|---|
| CWG 2079 | C++11 | [[가 속성 인자 안에 나타날 수 없었음 |
허용됨 |
| CWG 2538 | C++11 | 표준 속성을 문법적으로 무시할 수 있는지가 불명확했음 | 금지됨 |
| CWG 2695 | C++11 | 표준 속성을 의미상으로 무시할 수 있는지가 불명확했음 | 금지됨 |
| P2156R1 | C++11 | 모든 표준 속성이 attribute-list에 최대 한 번 나타나야 했음 | 요구하지 않음 |
더 알아보기
__has_cpp_attribute: 속성의 존재 여부 확인.- GCC, Clang, MSVC의 속성 문서: 각 컴파일러가 제공하는 비표준 속성들.