std 네임스페이스 확장하기

std 네임스페이스 확장하기 (Extending std)

표준 라이브러리의 std 네임스페이스에 **선언이나 정의를 추가하는 것은 정의되지 않은 동작(undefined behavior)**이에요. std 안에 중첩된 어떤 네임스페이스에도 마찬가지예요. 다만 아래에 적어둔 몇 가지 예외가 있어요.

출처: cppreference

본문

std에 선언 추가하기

std 네임스페이스나 그 안에 중첩된 어떤 네임스페이스에 선언이나 정의를 추가하는 것은 정의되지 않은 동작이에요. 단, 아래에 명시된 몇 가지 예외가 있어요.

#include <utility>

namespace std
{
    // a function definition added to namespace std: undefined behavior
    pair<int, int> operator+(pair<int, int> a, pair<int, int> b)
    {
        return {a.first + b.first, a.second + b.second};
    }
}

템플릿 특수화 추가하기

클래스 템플릿

표준 라이브러리 클래스 템플릿에 대한 템플릿 특수화를 std 네임스페이스에 추가하는 것은, 선언이 적어도 하나의 프로그램 정의 타입(program-defined type)에 의존하고 특수화가 원본 템플릿의 모든 요구 사항을 충족하는 경우에만 허용돼요. 그런 특수화가 금지되는 경우는 예외예요.

// Get the declaration of the primary std::hash template.
// We are not permitted to declare it ourselves.
// <typeindex> is guaranteed to provide such a declaration, 
// and is much cheaper to include than <functional>.

#include <typeindex> 

// Specialize std::hash so that MyType can be used as a key in 
// std::unordered_set and std::unordered_map.  Opening namespace
// std can accidentally introduce undefined behavior, and is not
// necessary for specializing class templates.
template<>
struct std::hash<MyType>
{
    std::size_t operator()(const MyType& t) const { return t.hash(); }
};
  • float, double, long double 외의 어떤 타입에 대해 std::complex 템플릿을 특수화하는 것은 미지정이에요.
  • std::numeric_limits의 특수화는 기본 템플릿에서 static const(C++11 이전) / static constexpr(C++11 이후)로 선언된 모든 멤버를, 정수 상수 표현식으로 사용 가능하도록 정의해야 해요.
  • (C++11 이후) <type_traits>에 정의된 템플릿 중 어떤 것도 프로그램 정의 타입에 대해 특수화될 수 없어요. 단 std::common_typestd::basic_common_reference(C++20)는 예외예요. 여기에는 타입 특성과 클래스 템플릿 std::integral_constant가 포함돼요.
  • 프로그램 정의 타입에 대한 std::hash 특수화는 Hash 요구 사항을 충족해야 해요.
  • std::atomic 특수화는 삭제된 복사 생성자, 삭제된 복사 할당 연산자, 그리고 constexpr 값 생성자를 가져야 해요.
  • std::shared_ptrstd::weak_ptr의 특수화는 CopyConstructible이고 CopyAssignable이어야 해요. 또한 std::shared_ptr의 특수화는 LessThanComparable이고 bool로 변환 가능해야 해요.
  • (C++11 이후) std::istreambuf_iterator의 특수화는 trivial 복사 생성자, constexpr 기본 생성자, trivial 소멸자를 가져야 해요.
  • (C++17 이전) std::unary_functionstd::binary_function은 특수화될 수 없어요.

표준 라이브러리 클래스 또는 클래스 템플릿의 멤버 클래스 템플릿의 전체 또는 부분 특수화를 선언하는 것은 정의되지 않은 동작이에요.

참고: 이 절은 불완전해요. 이유: mini-example.

함수 템플릿과 템플릿의 멤버 함수

(C++20 이전) 표준 라이브러리 함수 템플릿에 대한 템플릿 특수화를 std 네임스페이스에 추가하는 것은, 선언이 적어도 하나의 프로그램 정의 타입에 의존하고 특수화가 원본 템플릿의 모든 요구 사항을 충족하는 경우에만 허용돼요. 그런 특수화가 금지되는 경우는 예외예요.

(C++20 이후) 표준 라이브러리 함수 템플릿의 전체 특수화를 선언하는 것은 정의되지 않은 동작이에요.

참고: 이 절은 불완전해요. 이유: mini-example.

표준 라이브러리 클래스 템플릿의 멤버 함수의 전체 특수화를 선언하는 것은 정의되지 않은 동작이에요.

참고: 이 절은 불완전해요. 이유: mini-example.

표준 라이브러리 클래스 또는 클래스 템플릿의 멤버 함수 템플릿의 전체 특수화를 선언하는 것은 정의되지 않은 동작이에요.

참고: 이 절은 불완전해요. 이유: mini-example.

변수 템플릿

(C++14 이후) 표준 라이브러리 변수 템플릿의 전체 또는 부분 특수화를 선언하는 것은 정의되지 않은 동작이에요. 단 명시적으로 허용되는 경우는 예외예요.

참고: 이 절은 불완전해요. 이유: mini-example.

(C++20 이후) 다음은 허용되는 특수화들이에요:

  • std::disable_sized_sentinel_for, std::ranges::disable_sized_range, std::ranges::enable_view, std::ranges::enable_borrowed_range의 특수화는 상수 표현식에서 사용 가능해야 하고 **타입이 const bool**이어야 해요. 그리고
    • std::disable_sized_sentinel_for는 타입 SI 중 적어도 하나가 프로그램 정의 타입인, cv 한정되지 않은 비-배열 객체 타입 SI에 대해 특수화될 수 있어요.
    • std::ranges::disable_sized_range, std::ranges::enable_view, std::ranges::enable_borrowed_range는 cv 한정되지 않은 프로그램 정의 타입에 대해 특수화될 수 있어요.
  • 모든 수학 상수 변수 템플릿은 특수화가 프로그램 정의 타입에 의존한다면 부분 또는 명시적으로 특수화될 수 있어요.

템플릿의 명시적 인스턴스화

(C++20 이후) 표준 라이브러리에 정의된 클래스 템플릿을 명시적으로 인스턴스화하는 것은, 선언이 적어도 하나의 프로그램 정의 타입의 이름에 의존하고 인스턴스화가 원본 템플릿에 대한 표준 라이브러리 요구 사항을 충족하는 경우에만 허용돼요.

참고: 이 절은 불완전해요. 이유: mini-example.

기타 제한 (Other restrictions)

std 네임스페이스는 인라인 네임스페이스로 선언될 수 없어요.

주소 지정 제한 (Addressing restriction)

C++ 프로그램이 표준 라이브러리 함수 또는 표준 라이브러리 함수 템플릿의 인스턴스화에 대한 포인터, 참조(자유 함수와 정적 멤버 함수의 경우), 또는 멤버 포인터(비정적 멤버 함수의 경우)를 만들려고 명시적으로든 암시적으로든 시도하면, 그 동작은 **미지정(possibly ill-formed)**이에요. 단 그것이 **주소 지정 가능 함수(addressable function)**로 지정된 경우는 예외예요 (아래 참고).

다음 코드는 C++17에서 잘 정의됐지만, C++20부터 미지정 동작을 일으키고 컴파일에 실패할 수도 있어요:

#include <cmath>
#include <memory>

int main()
{
    // by unary operator&
    auto fptr0 = &static_cast<float(&)(float, float)>(std::betaf);

    // by std::addressof
    auto fptr1 = std::addressof(static_cast<float(&)(float, float)>(std::betaf));

    // by function-to-pointer implicit conversion
    auto fptr2 = static_cast<float(&)(float)>(std::riemann_zetaf);

    // forming a reference
    auto& fref = static_cast<float(&)(float)>(std::riemann_zetaf);
}

지정된 주소 지정 가능 함수 (Designated addressable functions)

  • I/O 조작자:
    • fmtflags 조작자: std::boolalpha, std::noboolalpha, std::showbase, std::noshowbase, std::showpoint, std::noshowpoint, std::showpos, std::noshowpos, std::skipws, std::noskipws, std::uppercase, std::nouppercase, std::unitbuf, std::nounitbuf
    • adjustfield 조작자: std::internal, std::left, std::right
    • basefield 조작자: std::dec, std::hex, std::oct
    • floatfield 조작자: std::fixed, std::scientific, std::hexfloat, std::defaultfloat
    • basic_istream 조작자: std::ws
    • basic_ostream 조작자: std::endl, std::ends, std::flush (C++20 이후 std::emit_on_flush, std::noemit_on_flush, std::flush_emit)

결함 보고 (Defect reports)

다음의 동작 변경 결함 보고는 이전에 발표된 C++ 표준에 소급 적용됐어요.

DR 적용 대상 발표 당시 동작 올바른 동작
LWG 120 C++98 사용자가 비-사용자 정의 타입에 대해 표준 라이브러리 템플릿을 명시적으로 인스턴스화할 수 있었음 금지됨
LWG 232 C++98 선언이 외부 링크를 가진 사용자 정의 이름(비-사용자 정의 타입을 가리킬 수 있음)에 의존하면 사용자가 표준 라이브러리 템플릿을 명시적으로 특수화할 수 있었음 사용자 정의 타입에만 허용
LWG 422 C++98 사용자가 표준 라이브러리 클래스/클래스 템플릿 전체를 특수화하지 않고 개별 멤버나 멤버 템플릿을 특수화할 수 있었음 이 경우 동작은 정의되지 않음

더 알아보기 (Learn more)