ranges_swap

ranges_swap (ranges::swap 사용자 지정 지점 객체)

이 페이지는 C++20에서 도입된 std::ranges::swap 사용자 지정 지점 객체에 대해 설명해요. ranges::swap(t, u)는 두 값 tu가 참조하는 값을 교환하는 표현식이에요. 이 함수는 다양한 유형의 인자에 대해 적절한 교환 방식을 자동으로 선택해요.

출처: cppreference

본문

정의

항목 내용
헤더 <concepts>
네임스페이스 namespace ranges { inline namespace /* unspecified */ { inline constexpr /* unspecified */ swap = /* unspecified */ ; } }
도입 (since C++20) (customization point object)
호출 시그니처 template < class T , class U > constexpr void ranges :: swap ( T && t , U && u ) noexcept ( /* see below */ ); (since C++20)

동작 방식

ranges::swap(t, u)tu가 참조하는 값을 교환해요. 이 표현식은 다음과 같은 규칙에 따라 결정돼요.

  • tu가 클래스 또는 열거형 타입이고 해당 표현식이 유효하다면, (void) swap(t, u)와 동일해요. 이때 오버로드 해석은 std::ranges 네임스페이스 안에서 추가 후보 template<class T> void swap(T&, T&) = delete;와 함께 수행돼요. 오버로드 해석으로 선택된 함수가 tu가 참조하는 값을 교환하지 않는다면, 프로그램은 진단 없이 ill-formed예요.

  • 그렇지 않고, tu가 같은 크기의 lvalue 배열이고(요소 타입은 달라도 돼요) ranges::swap(*t, *u)가 유효한 표현식이라면, (void) ranges::swap_ranges(t, u)와 동일해요. 단, noexcept((void) ranges::swap_ranges(t, u))noexcept(ranges::swap(*t, *u))와 같아요.

  • 그렇지 않고, tu가 모두 std::move_constructible<V>std::assignable_from<V&, V>를 만족하는 같은 타입 V의 lvalue라면, tu가 참조하는 값을 교환하는 표현식과 동일해요. 그 표현식에 noexcept 연산자를 적용한 결과는 std::is_nothrow_move_constructible_v<V> && std::is_nothrow_move_assignable_v<V>와 같아요. V가 LiteralType이고, t = std::move(u)u = std::move(t)가 모두 상수 부분 표현식이며, 다음 선언들의 초기화 전체 표현식이 상수 부분 표현식이라면 그 표현식은 상수 표현식이에요: V v1(std::move(t)); V v2(std::move(u));

  • 그 외의 경우 ranges::swap(t, u)는 ill-formed예요. 이는 ranges::swap(t, u)가 템플릿 인스턴스화의 immediate context에 나타날 때 치환 실패(substitution failure)로 이어질 수 있어요.

사용자 지정 지점 객체

ranges::swap이라는 이름은 사용자 지정 지점 객체(customization point object)를 나타내요. 이는 리터럴 세미레귤러 클래스 타입의 const 함수 객체예요. 자세한 내용은 CustomizationPointObject를 참고하세요.

예제

#include <array>
#include <concepts>
#include <iostream>
#include <ranges>
#include <string_view>
#include <vector>

void print(std::string_view name, 
           std::ranges::common_range auto const& p, 
           std::ranges::common_range auto const& q)
{
    std::cout << name << "1{ ";
    for (auto const& i : p)
        std::cout << i << ' ';
    std::cout << "}, " << name << "2{ ";
    for (auto const& i : q)
        std::cout << i << ' ';
    std::cout << "}\n";
}

void print(std::string_view name, int p, int q)
{
    std::cout << name << "1 = " << p << ", " << name << "2 = " << q << '\n';
}

struct IntLike
{
    int v;
};

void swap(IntLike& lhs, int& rhs)
{
    std::swap(lhs.v, rhs);
}

void swap(int& lhs, IntLike& rhs)
{
    std::swap(lhs, rhs.v);
}

std::ostream& operator<<(std::ostream& out, IntLike i)
{
    return out << i.v;
}

int main()
{
    std::vector a1{10, 11, 12}, a2{13, 14};
    std::ranges::swap(a1, a2);
    print("a", a1, a2);
    
    std::array b1{15, 16, 17}, b2{18, 19, 20};
    std::ranges::swap(b1, b2);
    print("b", b1, b2);
    
    // std::array c1{1, 2, 3}; std::array c2{4, 5};
    // std::ranges::swap(c1, c2); // error: no swap found by ADL
    
    int d1[]{21, 22, 23}, d2[]{24, 25, 26};
    std::ranges::swap(d1, d2);
    print("d", d1, d2);
    
    // int e1[]{1, 2, 3}, e2[]{4, 5};
    // std::ranges::swap(e1, e2); // error: extents mismatch
    
    // char f1[]{1, 2, 3};
    // int  f2[]{4, 5, 6};
    // std::ranges::swap(f1, f2); // error: no swap(*f1, *f2) found by ADL
    
    IntLike g1[]{1, 2, 3};
    int     g2[]{4, 5, 6};
    std::ranges::swap(g1, g2); // heterogeneous swap supported
    print("g", g1, g2);
    
    int h1{27}, h2{28};
    std::ranges::swap(h1, h2);
    print("h", h1, h2);
}

출력:

a1{ 13 14 }, a2{ 10 11 12 }
b1{ 18 19 20 }, b2{ 15 16 17 }
d1{ 24 25 26 }, d2{ 21 22 23 }
g1{ 4 5 6 }, g2{ 1 2 3 }
h1 = 28, h2 = 27

같이 보기

항목 설명
swappable, swappable_with (C++20) 한 타입이 교환 가능하거나 두 타입이 서로 교환 가능함을 지정하는 개념 (concept)
swap 두 객체의 값을 교환하는 함수 템플릿

더 알아보기 (Learn more)

cppreference