basic_ostream::operator<<

basic_ostream::operator<< (문자 · 문자열 삽입)

출력 스트림에 문자나 문자 배열을 삽입하는 삽입 연산자(<<)들이에요. std::basic_ostream 계열이 문자·문자열 출력을 처리하는 방법을 정의해요. C++11부터 rvalue 스트림 버전도 있어요.

출처: cppreference

본문

<ostream> 헤더에 정의돼 있고, 문자 계열 삽입용 오버로드예요.

template< class CharT, class Traits >
basic_ostream<CharT, Traits>&
    operator<<( basic_ostream<CharT, Traits>& os, CharT ch );   // (1)

template< class CharT, class Traits >
basic_ostream<CharT, Traits>&
    operator<<( basic_ostream<CharT, Traits>& os, const CharT* s ); // (2)

template< class Ostream, class T >
Ostream&& operator<<( Ostream&& os, const T& value );           // (3) (since C++11)
  • (1) 문자를 삽입해요.
  • (2) 문자 배열에서 s가 가리키는 첫 원소부터 연속된 문자들을 삽입해요.
  • (3) 출력 스트림 객체의 rvalue 참조가 주어졌을 때 적절한 삽입 연산자를 호출해요(os << value와 같음). os << value 식이 well-formed이고 Ostreamstd::ios_base에서 공개적이고 명확하게 파생된 클래스 타입일 때만 오버로드 해석에 참여해요.
  • (4) (C++20부터) wchar_t, char8_t, char16_t, char32_t 등(또는 그 널 종료 시퀀스)을 받는 오버로드는 삭제돼요. std::cout << u'X'는 허용되지 않아요. 이전에는 이게 정수나 포인터 값을 출력했어요.

(1)은 FormattedOutputFunction처럼 동작해요. sentry 객체를 만들고 검사한 뒤 문자 ch를 삽입해요. chchar 타입이고 os의 문자 컨테이너 타입이 char가 아니면 os.widen(ch)가 대신 삽입돼요.

패딩은 다음과 같이 결정돼요. os.width() > 1이면 os.width() - 1개만큼의 os.fill() 복사본이 출력 문자에 더해져 출력 문자 시퀀스를 만들어요. (out.flags() & std::ios_base::adjustfield) == std::ios_base::left면 채움 문자가 출력 문자 뒤에, 아니면 앞에 배치돼요. 삽입 후 os.width(0)을 호출해 std::setw의 효과가 있으면 취소해요.

(2)에서는 삽입 전에 먼저 모든 문자를 os.widen()으로 넓힌 뒤 패딩을 결정해요. 삽입할 문자 수가 os.width()보다 작으면 문자 시퀀스 길이가 os.width()가 되도록 충분한 os.fill() 복사본을 더해요. s가 널 포인터면 동작이 정의되지 않아요.

  • 매개변수 os: 데이터를 삽입할 출력 스트림. ch: 삽입할 문자. s: 삽입할 문자 배열 포인터.
  • 반환 값: (1,2) os, (3) std::move(os).

참고로 LWG 1203 이전에는 (std::ostringstream() << 1.2).str() 같은 코드가 컴파일되지 않았어요. rvalue 스트림 오버로드가 추가되면서 이제 동작해요.

예제를 보면요.

#include <fstream>
#include <iostream>

std::ostream& operator<<(std::ostream& os, char8_t const& ch)
{
    return os << static_cast<char>(ch);
}

int main()
{
    std::cout << "Hello, world" // `const char*` 오버로드 사용
              << '\n';          // `char` 오버로드 사용
    std::ofstream{"test.txt"} << 1.2; // rvalue 오버로드 사용

    std::cout << u8'!' << '\n'; // 사용자 정의 operator<<(os, char8_t const&) 사용
}

결함 보고로 LWG 167(C++98, (2)의 모든 오버로드에서 삽입 문자가 traits::length(s)였음 → CharTch 타입과 일치하지 않는 오버로드의 수를 갱신), LWG 1203(C++11, rvalue 스트림 오버로드가 기반 클래스의 lvalue 참조 반환 → 파생 클래스의 rvalue 참조 반환), LWG 2011(C++98, 패딩이 std::num_put::do_put()으로 결정됨 → 연산자 자신이 결정), LWG 2534(C++11, rvalue 오버로드가 제약되지 않음 → 제약 추가) 등이 있어요.

더 알아보기 (Learn more)

cppreference