<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>creat_my_world.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Thu, 26 Mar 2026 11:37:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. creat_my_world.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/creat_my_world" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[type_traits]]></title>
            <link>https://velog.io/@creat_my_world/typetraits</link>
            <guid>https://velog.io/@creat_my_world/typetraits</guid>
            <pubDate>Thu, 26 Mar 2026 11:37:21 GMT</pubDate>
            <description><![CDATA[<h1 id="type_traits">type_traits</h1>
<p>타입들에 대해 여러가지 연산을 수행할 수 있는 메타 함수들을 제공하고 있다.</p>
<h3 id="static_assert">static_assert</h3>
<p>static_assert(bool 타입의 constexpr) 
전달된 식이 참이라면 컴파일러에 의해 해당 식은 무시되고 거짓이라면 해당 문장에서 컴파일 오류 발생시킴</p>
<h3 id="integral_constantt-t-v">integral_constant&lt;T, T v&gt;</h3>
<p>v를 static 인자로 가지는 클래스 -&gt; 어떠한 값을 static 객체로 가지고 있는 클래스를 만들어주는 템플릿</p>
<h3 id="데이터-멤버를-가리키는-포인터pointer-to-data-member">데이터 멤버를 가리키는 포인터(Pointer to Data member)</h3>
<p>클래스와 공용체에서만 사용가능</p>
<pre><code class="language-cpp">int T::* //T의 int멤버를 가리키는 포인터</code></pre>
<pre><code class="language-cpp">class A
{
public:
    int n;

    A(int n) :n(n){}


};


int main()
{
    int A::* p_n = &amp;A::n;

    A a(3);


    std::cout &lt;&lt; a.n &lt;&lt; &quot;\n&quot;;
    std::cout &lt;&lt; a.*p_n;

    return 0;
}</code></pre>
<h3 id="sfinae치환오류는-컴파일-오류가-아니다">SFINAE(치환오류는 컴파일 오류가 아니다)</h3>
<p>컴파일 오류 발생 대신 함수의 오버로딩 후보군에서만 제외된다.</p>
<h3 id="enable_if">enable_if</h3>
<pre><code class="language-cpp">template&lt;bool B, class T = void&gt;
struct enable_if;</code></pre>
<p>B가 참일때 enable_if::value의 타입이 T가 되고
B가 거짓일때 value가 존재하지 않게 된다.</p>
<h2 id="1-기반-다지기ai-사용">1. 기반 다지기(ai 사용)</h2>
<h3 id="type_traits와-integral_constant">type_traits와 integral_constant</h3>
<p>C++에서 타입에 대한 정보를 컴파일 타임에 알아내고 조작하려면, &#39;값(value)&#39;을 &#39;타입(type)&#39;으로 승격시키는 작업이 필요합니다.</p>
<p>std::integral_constant&lt;T, v&gt;: 변수 v(값)를 템플릿 인자로 받아 하나의 독립된 타입으로 만들어주는 래퍼(Wrapper)입니다.</p>
<h4 id="왜-필요한가">왜 필요한가?</h4>
<p>템플릿 메타프로그래밍에서는 런타임의 if (bool) 대신, 컴파일 타임의 타입 매칭을 통해 분기를 태워야 합니다. 그래서 integral_constant&lt;bool, true&gt;를 std::true_type으로, false를 std::false_type으로 미리 정의해 둡니다.</p>
<h4 id="연결">연결</h4>
<p><type_traits> 헤더에 있는 수많은 메타 함수들(예: is_pointer, is_arithmetic)은 조건을 만족하면 true_type을, 아니면 false_type을 상속받도록 구현되어 있습니다.</p>
<h2 id="2-안전망-구축-static_assert">2. 안전망 구축: static_assert</h2>
<p>템플릿은 아무 타입이나 다 받아주기 때문에 잘못된 타입이 들어오면 에러 메시지가 걷잡을 수 없이 길어집니다.</p>
<h4 id="역할">역할</h4>
<p>컴파일 타임의 assert입니다. 주로 type_traits와 결합하여 입력된 타입이 내가 원하는 조건이 맞는지 입구 컷을 담당합니다.</p>
<h4 id="활용">활용</h4>
<p>예를 들어, 네트워크 패킷이나 게임 세이브 데이터를 고속으로 복사(memcpy)하는 템플릿 함수를 만들 때, static_assert(std::is_trivially_copyable_v<T>, &quot;안전하게 복사할 수 없는 타입입니다!&quot;);라고 선언하여 치명적인 런타임 메모리 버그를 컴파일 타임에 차단합니다.</p>
<h2 id="3-구조적-유연성">3. 구조적 유연성</h2>
<h4 id="데이터-멤버를-가리키는-포인터-type">데이터 멤버를 가리키는 포인터 (Type::*)</h4>
<p>일반 포인터가 메모리의 특정 <strong>&#39;절대 주소&#39;</strong>를 가리킨다면, 멤버 포인터는 클래스 시작점으로부터의 <strong>&#39;상대적 위치(Offset)&#39;</strong>를 가리킵니다.</p>
<h4 id="핵심">핵심</h4>
<p>  객체가 아직 생성되지 않았어도(인스턴스가 없어도), 클래스 내부에 어떤 변수들이 있는지를 가리킬 수 있습니다. 사용할 때는 반드시 실제 인스턴스(a.<em>p_n)나 포인터(pa-&gt;</em>p_n)가 필요합니다.</p>
<h4 id="활용-1">활용</h4>
<p>  엔진의 리플렉션(Reflection) 시스템이나 인스펙터(UI) 창을 구현할 때 핵심이 됩니다. &quot;Player 클래스의 hp 변수 위치&quot;를 멤버 포인터로 맵핑해두면, 나중에 어떤 Player 인스턴스가 들어오든 범용적인 코드로 hp 값을 읽고 쓸 수 있습니다.</p>
<h2 id="4-컴파일-타임-분기의-꽃-sfinae와-enable_if">4. 컴파일 타임 분기의 꽃: SFINAE와 enable_if</h2>
<p>이 모든 것을 조합하여 컴파일러가 알아서 알맞은 함수를 선택하게 만드는 마법입니다.</p>
<h4 id="sfinae-치환-실패는-에러가-아니다">SFINAE (치환 실패는 에러가 아니다)</h4>
<p>  컴파일러가 템플릿 타입(T)을 추론하여 끼워 넣다가 문법적으로 말이 안 되는 상황이 발생하면, 에러를 내뿜고 죽는 대신 &quot;어? 이 함수는 아니네? 다른 오버로딩 함수 찾아볼까?&quot; 하고 쿨하게 넘어가는 규칙입니다.</p>
<h4 id="stdenable_ifb-t">std::enable_if&lt;B, T&gt;:</h4>
<p>  SFINAE 규칙을 우리가 원하는 대로 조종하기 위한 스위치입니다.</p>
<p>조건 B(주로 type_traits의 결과)가 <strong>참(true)</strong>이면: 내부에 type이라는 멤버(T)가 생성됩니다. (치환 성공 -&gt; 함수 후보 등록)</p>
<p>조건 B가 <strong>거짓(false)</strong>이면: 내부에 type이 아예 정의되지 않습니다. 컴파일러가 ::type을 찾으려다 실패하므로 SFINAE가 발동합니다. (치환 실패 -&gt; 함수 후보에서 조용히 탈락)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Value Category]]></title>
            <link>https://velog.io/@creat_my_world/Value-Category</link>
            <guid>https://velog.io/@creat_my_world/Value-Category</guid>
            <pubDate>Thu, 26 Mar 2026 10:39:31 GMT</pubDate>
            <description><![CDATA[<h1 id="value-category">Value Category</h1>
<p>  이 데이터에 이름이 있고 주소를 취할 수 있는가(정체성) / 이동시킬 수 있는가(이동 생성자, 이동 대입 연산자등 사용여부)에 대한 분류이다.</p>
<h2 id="값-범주-value-category의-종류">값 범주 (Value Category)의 종류</h2>
<h3 id="3대-기본-범주-primary-categories">3대 기본 범주 (Primary Categories)</h3>
<h4 id="좌측값-lvalue">좌측값 (lvalue)</h4>
<pre><code>정의: 이름을 가지고 있으며, 메모리 주소를 얻을 수 있는 대상

특징: 표현식이 끝나도 사라지지 않고 유지

예시: 변수 이름(x), 객체의 멤버(p.hp), 역참조된 포인터(*ptr)</code></pre><h4 id="순수-우측값-prvalue---pure-rvalue">순수 우측값 (prvalue - pure rvalue)</h4>
<pre><code>정의: 이름이 없고 곧 사라질 임시 값

특징: 주소를 취할 수 없으며, 보통 리터럴이나 함수의 반환값(값 타입)

예시: 42, true, a + b, getNumber()</code></pre><h4 id="소멸-예정-값-xvalue---expiring-value">소멸 예정 값 (xvalue - expiring value)</h4>
<pre><code>정의: 이름은 있을 수 있지만, 곧 사라질 것이니 자원을 뺏어가도(Move) 좋다고 표시된 값

특징: std::move()를 사용했을 때의 결과물이 대표적

예시: std::move(x)</code></pre><h3 id="2대-복합-범주-mixed-categories">2대 복합 범주 (Mixed Categories)</h3>
<p>위의 세 가지를 묶어서 부르는 &#39;그룹명&#39;, 컴파일러가 규칙을 적용할 때 주로 사용</p>
<h4 id="glvalue-generalized-lvalue">glvalue (Generalized lvalue)</h4>
<pre><code>정체성(Identity)을 가진 모든 것 (lvalue + xvalue)

&quot;메모리에 실체가 있으니, 다형성(가상 함수)을 쓸 수 있음</code></pre><h4 id="rvalue-right-value">rvalue (Right value)</h4>
<pre><code>이동(Movability)이 가능한 모든 것 (prvalue + xvalue)

(Move 생성자/대입 연산자 호출) </code></pre><pre><code class="language-cpp">class Vector3 {
public:
    float x, y, z;
    Vector3(float x, float y, float z) : x(x), y(y), z(z) {}
};

Vector3 make_forward_vector() { return Vector3(0, 0, 1); }

int main() {
    Vector3 v1(1, 0, 0);               // v1은 lvalue


    Vector3 v2 = make_forward_vector(); // prvalue (공간에 떠 있는 임시 값)

    Vector3 v3 = std::move(v1);         // xvalue (v1이라는 실체가 있지만 이동 허락됨)


    float z = make_forward_vector().z;  // xvalue입니다!
    // (prvalue인 임시 객체의 멤버에 접근하는 순간, 메모리 실체가 필요해져서 xvalue로 승격됨)
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[decltype, declval]]></title>
            <link>https://velog.io/@creat_my_world/decltype-declval</link>
            <guid>https://velog.io/@creat_my_world/decltype-declval</guid>
            <pubDate>Thu, 26 Mar 2026 09:05:26 GMT</pubDate>
            <description><![CDATA[<h1 id="decltype">decltype</h1>
<p>타입을 알고자 하는 식의 타입으로 치환되게 됨</p>
<pre><code class="language-cpp">int a = 9;
decltype(a) b = 2; // int

int &amp;r_a = a;
decltype(r_a) r_b = b; // int&amp;

int &amp;&amp;x = 3;
decltype(x) y = 2; // int&amp;
</code></pre>
<p>괄호로 둘러쌓이지 않은 <strong>식별자 표현식(id-expression)</strong>일 경우 해당식의 타입을 얻을 수 있다.</p>
<h2 id="식별자-표현식id-expression이란">식별자 표현식(id-expression)이란?</h2>
<blockquote>
<p>개별 엔티티(변수, 함수, 네임스페이스 등)를 식별하기 위해 사용하는 모든 명칭이 식별자 표현식에 해당한다.</p>
</blockquote>
<h3 id="주-식별자-표현식-primary-id-expression">주 식별자 표현식 (Primary id-expression)</h3>
<pre><code>변수명: int score = 100;에서 score

함수명: void move();에서 move

열거체 상수: enum Color { RED };에서 RED</code></pre><h3 id="정규화된-식별자-표현식-qualified-id-expression">정규화된 식별자 표현식 (Qualified id-expression)</h3>
<p>   범위 결정 연산자(::)를 사용하여 이름이 속한 위치를 명확히 밝힌 형태</p>
<pre><code>네임스페이스 지정: std::cout, std::vector

클래스 멤버 지정: Player::Move, MyClass::value

전역 범위 지정: ::globalVar (전역 공간에 있는 이름을 명시적으로 가리킬 때)</code></pre><p>   만약 식별자 표현식이 아닌 다른 식을 전달할 경우? 
   ex) a + b 
   -&gt; 값의 종류(value category)에 따라 달라지게 된다.</p>
<h2 id="decltype의-쓰임">decltype의 쓰임</h2>
<p> 식의 값 종류가 xvalue일 경우 decltype 은 T &amp;&amp; 가 된다.
  식의 값 종류가 lvalue일 경우 decltype 은 T &amp; 가 된다.
   식의 값 종류가 prvalue일 경우 decltype 은 T 가 된다.</p>
<pre><code class="language-cpp">int a;
decltype((a)) b;</code></pre>
<p>  (a)는 식별자 표현식이 아님
  이동 불가능 + &amp;(a)형태로 적용가능 하므로 lvalue이다 
  즉, int가 아닌 int&amp; 로 추론된다.</p>
<h3 id="auto와-차이점">auto와 차이점</h3>
<p>  <img src="https://velog.velcdn.com/images/creat_my_world/post/dde4316f-7a40-4045-b38e-6a8c31ac9272/image.png" alt=""></p>
<h4 id="const-보존">const 보존</h4>
<pre><code class="language-cpp">const int&amp; ref = x;

auto a = ref;       // a는 그냥 int (const와 &amp;가 떨어져 나감 - 값 복사)
decltype(ref) d = ref; // d는 const int&amp; (원래 타입 그대로 유지)</code></pre>
<h4 id="배열-그대로-전달">배열 그대로 전달</h4>
<pre><code class="language-cpp">int arr[10];
auto arr2 = arr; //int *arr2 = arr;
decltype(arr) arr3; //int arr3[10];</code></pre>
<p>즉, decltype은 auto와 다르게 원래 형태를 그대로 유지한다.</p>
<pre><code class="language-cpp">template&lt;typename T, typename U&gt;
void add(T t, U u, decltype(t + u)* result)
{
    *result = t + u;
}</code></pre>
<p>만약 반환형을 decltype(t + u)로 할 경우엔 컴파일 오류가 발생하는데 t와 u의 정의가 decltype 보다 나중에 나오기 때문에<strong>(C++ 컴파일러는 코드를 위에서 아래로, 왼쪽에서 오른쪽으로 읽으며 해석)</strong> 함수의 리턴값을 인자들 정의 부분 나중에 두어야함.</p>
<pre><code class="language-cpp">template&lt;typename T, typename U&gt;
auto add(T t, U u) -&gt; decltype(t + u)
{
    *result = t + u;
}</code></pre>
<h1 id="declval">declval</h1>
<blockquote>
<p>std::declval은 C++ 템플릿 프로그래밍(Template Meta-Programming, TMP)에서 &quot;<strong>실제로 객체를 생성하지 않고도, 특정 타입의 객체가 있다고 가정하고 그 멤버나 연산 결과의 타입을 알아내기 위해</strong>&quot; 사용하는 도구이다</p>
</blockquote>
<p><strong>std::declval<T>()</strong>는 타입 T에 대한 <strong>우측값 참조(T&amp;&amp;)</strong>를 반환하는 함수 형태의 템플릿이다.
  실제로 호출해서 실행할 수는 없고, 오직 decltype이나 sizeof 같은 <strong>컴파일 타임 연산 안에서만 쓰인</strong>다.</p>
<h3 id="도입-이유">도입 이유?</h3>
<p>  어떤 타입의 멤버 함수 반환 타입을 알고 싶을 때, 그 타입에 기본 생성자가 없다면 문제가 생기게 된다.</p>
<pre><code class="language-cpp">struct Player {
    Player(int id) {} // 기본 생성자가 없음
    int getHp() { return 100; }
};

// Player의 getHp()가 뭘 반환하는지 알고 싶어서 이렇게 썼다면?
decltype(Player().getHp()) myHp; // 에러! Player()는 기본 생성자가 없어서 호출 불가.</code></pre>
<pre><code class="language-cpp">#include &lt;utility&gt;

// 만약 Player 객체가 있다면, getHp()는 뭘 반환할지
decltype(std::declval&lt;Player&gt;().getHp()) myHp; // myHp는 int 타입</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[컴파일 타임 상수(constexpr)]]></title>
            <link>https://velog.io/@creat_my_world/%EC%BB%B4%ED%8C%8C%EC%9D%BC-%ED%83%80%EC%9E%84-%EC%83%81%EC%88%98constexpr</link>
            <guid>https://velog.io/@creat_my_world/%EC%BB%B4%ED%8C%8C%EC%9D%BC-%ED%83%80%EC%9E%84-%EC%83%81%EC%88%98constexpr</guid>
            <pubDate>Thu, 12 Mar 2026 12:35:15 GMT</pubDate>
            <description><![CDATA[<h2 id="constexpr">constexpr</h2>
<p>이 키워드가 붙은 객체나 함수의 리턴 값을 <strong>컴파일 타임</strong>에 알 수 있다는 의미를 가짐 </p>
<pre><code class="language-cpp">
template&lt;int N&gt;
struct A
{
    int operator()() {return N;}
}

int main()
{
    constexpr int num = 3;
    int arr[num];

    constexpr int N = 10;
    A&lt;N&gt; a;
    std::cout &lt;&lt; a();

    constexpr int val = 10;
    enum B { x = val ,y ,z}
}
</code></pre>
<p>어떠한 식이 상수식이라고 명시해주는 키워드</p>
<h2 id="constexpr-vs-const">constexpr vs const</h2>
<p>const로 정의된 상수들은 굳이 컴파일 타임에 그 값을 알 필요가 없음.</p>
<pre><code class="language-cpp">int a;
...
const int b = a;
</code></pre>
<pre><code class="language-cpp">int a;
...
constexpr int b = a; //컴파일 타임 시 a가 뭐가 올 지 모르기 때문에 오류 발생
</code></pre>
<h2 id="constexpr-함수">constexpr 함수</h2>
<h3 id="제약조건">제약조건</h3>
<ol>
<li>goto 문 사용</li>
<li>예외 처리(trya문 C++ 20 부터 가능)</li>
<li>리터럴 타입이 아닌 변수의 정의</li>
<li>초기화 되지 않는 변수의 정의</li>
<li>실행 중간에 constexpr이 아닌 함수를 호출하게 됨
<img src="https://velog.velcdn.com/images/creat_my_world/post/24468dc6-bb8f-403d-a9c6-23f81d6f2b7a/image.png" alt=""></li>
</ol>
<h2 id="constexpr-생성자">constexpr 생성자</h2>
<p>constexpr함수에서 적용되는 제약이 동일하게 적용된다. 이 클래스는 다른 클래스를 가상 상속 받을 수 없다.</p>
<h2 id="if-constexpr">if constexpr</h2>
<p>&quot;컴파일 타임에 조건문을 평가해서 조건에 맞지 않는 코드 블록을 아예 삭제해버리는&quot; 기능</p>
<h3 id="일반-if와-무엇이-다른가요">일반 if와 무엇이 다른가요?</h3>
<p>가장 큰 차이점은 컴파일 에러 발생 여부입니다.</p>
<p>*<em>일반 if: *</em>조건이 false라도 그 안의 코드가 문법적으로 틀리면 무조건 컴파일 에러가 납니다.</p>
<p><strong>if constexpr:</strong> 조건이 false인 블록은 컴파일러가 아예 무시(Discard)합니다. 따라서 그 안에 말도 안 되는 코드가 있어도(템플릿 타입과 맞지 않는 등) 컴파일 에러가 나지 않습니다.</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void show_value(T t)
{
    if constexpr (std::is_pointer&lt;T&gt;::value) 
    {
        std::cout &lt;&lt; &quot;포인터 이다 : &quot; &lt;&lt; *t &lt;&lt; std::endl;
    }
    else
    {
        std::cout &lt;&lt; &quot;포인터가 아니다 : &quot; &lt;&lt; t &lt;&lt; std::endl;
    }
}

int main()
{

    int x = 3;
    show_value(x);/*constexpr이 없을 경우 조건문 내 블럭을 검사하는데 
   이때 x가 int로 들어왔으므로 *t는 포인터가 아닌 것을 
   역참조 하려하기 때문에 오류가 발생한다. */

    int* p = &amp;x;
    show_value(p);


    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[균일한 초기화(Uniform Initialization)와
 초기화자 리스트(Initializer List)]]></title>
            <link>https://velog.io/@creat_my_world/%EA%B7%A0%EC%9D%BC%ED%95%9C-%EC%B4%88%EA%B8%B0%ED%99%94Uniform-Initialization%EC%99%80-%EC%B4%88%EA%B8%B0%ED%99%94%EC%9E%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8Initializer-List</link>
            <guid>https://velog.io/@creat_my_world/%EA%B7%A0%EC%9D%BC%ED%95%9C-%EC%B4%88%EA%B8%B0%ED%99%94Uniform-Initialization%EC%99%80-%EC%B4%88%EA%B8%B0%ED%99%94%EC%9E%90-%EB%A6%AC%EC%8A%A4%ED%8A%B8Initializer-List</guid>
            <pubDate>Thu, 12 Mar 2026 11:32:26 GMT</pubDate>
            <description><![CDATA[<h2 id="1-도입-이유">1. 도입 이유</h2>
<p>C++11 이전에는 객체를 초기화하는 방법이 너무 파편화되어 있었습니다.</p>
<p>어떤 건 ()를 쓰고, 어떤 건 {}를 쓰고, 어떤 건 =를 썼죠.</p>
<p>특히 <strong>&quot;가장 성가신 구문 분석(Most Vexing Parse)&quot;</strong>이라는 치명적인 문법적 모호함이 있었습니다.</p>
<pre><code class="language-cpp">// C++11 이전의 비극
struct Player { Player(int id) {} };

Player p1(1);      // 객체 생성
Player p2();       // ⚠️ 주의: 객체 생성이 아니라 &#39;함수 선언&#39;으로 해석됨!</code></pre>
<h2 id="2-균일한-초기화-uniform-initialization">2. 균일한 초기화 (Uniform Initialization)</h2>
<p>모든 데이터 타입을 중괄호 { } 하나로 초기화하는 방식입니다.</p>
<p>특징
일관성: 일반 변수, 배열, 구조체, 클래스 모두 {}로 통일 가능합니다.</p>
<p>함수 선언 방지: Player p{};라고 쓰면 컴파일러가 이를 절대 함수 선언으로 오해하지 않습니다.</p>
<p>좁게 만들기 방지(Narrowing Conversion): 데이터 손실이 발생하는 암시적 형변환을 컴파일 단계에서 막아줍니다. (ex. float to int)</p>
<pre><code class="language-cpp">int a = 3.14;    // (경고만 뜨고) a는 3이 됨. 데이터 손실 발생.
int b{ 3.14 };   // ❌ 컴파일 에러! 데이터 손실을 허용하지 않음 (안전함)</code></pre>
<h2 id="3-초기화자-리스트-stdinitializer_list">3. 초기화자 리스트 (std::initializer_list)</h2>
<p>여러 개의 동일한 타입 데이터를 묶어서 생성자에 전달할 수 있게 해주는 도구입니다.</p>
<p>특징
가변 인자 처리: {1, 2, 3, 4} 처럼 개수가 정해지지 않은 리스트를 받을 수 있습니다.</p>
<p>컨테이너 지원: std::vector나 std::map에 데이터를 한꺼번에 넣을 수 있는 이유가 바로 이것 때문입니다.</p>
<pre><code class="language-cpp">std::vector&lt;int&gt; v = {1, 2, 3, 4, 5}; // 초기화자 리스트 덕분에 가능</code></pre>
<h2 id="4-⚠️-주의점-함정-카드">4. ⚠️ 주의점 (함정 카드)</h2>
<p>가장 중요합니다. 생성자에 std::initializer_list를 받는 오버로딩이 있다면, <strong>중괄호 초기화는 무조건 이 리스트를 우선적으로 선택</strong>합니다.</p>
<pre><code class="language-cpp">class MyVector {
public:
    MyVector(int size, int value) { /* 사이즈만큼 초기화 */ }
    MyVector(std::initializer_list&lt;int&gt; list) { /* 리스트로 초기화 */ }
};


MyVector v1(10, 2); // 첫 번째 생성자 호출 (사이즈 10, 값 2)
MyVector v2{10, 2}; // ⚠️ 두 번째 생성자 호출! (데이터 10과 2가 들어있는 리스트)</code></pre>
<p>이 규칙 때문에 std::vector<int> v{10, 20};을 하면 10개짜리 벡터가 아니라, 10과 20이 들어있는 2개짜리 벡터가 만들어집니다.</p>
<h2 id="auto와-사용시">auto와 사용시</h2>
<p>  auto x{ 1 }; 처럼 대입 연산자 없이 쓰면 우리가 원하는 일반 타입이 나옵니다. x{1 ,2}처럼 인자 2개 이상 시 오류</p>
<p>auto x = { 1 }; 처럼 대입 연산자와 함께 쓰면 <strong>std::initializer_list</strong>가 나옵니다 (같은 타입)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[잡다한 것]]></title>
            <link>https://velog.io/@creat_my_world/%EC%9E%A1%EB%8B%A4%ED%95%9C-%EA%B2%83</link>
            <guid>https://velog.io/@creat_my_world/%EC%9E%A1%EB%8B%A4%ED%95%9C-%EA%B2%83</guid>
            <pubDate>Thu, 12 Mar 2026 08:42:56 GMT</pubDate>
            <description><![CDATA[<h2 id="stdthread가-값을-반환하지-못하는-이유">std::thread가 값을 반환하지 못하는 이유</h2>
<p>std::thread는 하위 레벨의 운영체제 스레드를 직접 다루는 객체입니다. 스레드가 생성되어 함수를 실행하고 나면, 그 결과값을 어디에 저장해야 할지 std::thread 자체는 알 방법이 없습니다.</p>
<p><strong>비동기성:</strong> 스레드는 자기가 할 일을 다 하고 언제 끝날지 모릅니다. 호출한 쪽(Main)에서 결과값을 받으려 할 때, 스레드가 이미 종료되어 메모리가 사라졌을 수도 있죠.</p>
<p><strong>인터페이스:</strong> std::thread 객체의 생성자는 어떤 인자든 받을 수 있게 가변 인자 템플릿으로 되어 있지만, 결과적으로 실행하는 내부 구조는 void를 지향합니다.</p>
<blockquote>
<p>-&gt; 그래서 우리가 promise, future, packaged_task를 쓴 것이다.</p>
</blockquote>
<p> 스레드가 직접 값을 못 돌려주기 때문에, 우리는 <strong>&quot;우회로&quot;</strong>를 만든 것입니다.</p>
<p>std::promise: &quot;스레드야, 너 함수 끝나면 여기에 결과값 좀 써줘.&quot; (입구)</p>
<p>std::future: &quot;난 여기서 기다렸다가 그 결과값이 들어오면 가져갈게.&quot; (출구)</p>
<p>std::packaged_task: (오늘의 핵심) 이 녀석이 바로 &quot;반환값이 있는 함수를 받아서, 내부적으로 void형 함수로 변신시킨 뒤, 결과를 future에 채워주는&quot; 마법의 상자입니다.</p>
<h2 id="vector-push_back--emplace_back-차이">Vector/ push_back , emplace_back 차이</h2>
<h3 id="push_back-복사-또는-이동">push_back (복사 또는 이동)</h3>
<p>방식: 함수 외부에서 이미 생성된 객체를 인자로 받습니다.</p>
<p>과정: 1. 임시 객체가 생성됨.
2. push_back이 호출되면서 벡터 내부 공간으로 객체를 <strong>복사(Copy)</strong>하거나 <strong>이동(Move)</strong>함.
3. 외부의 임시 객체는 소멸됨</p>
<h3 id="emplace_back-직접-생성">emplace_back (직접 생성)</h3>
<p>방식: 객체 자체가 아니라, 객체를 생성하는 데 필요한 인자들만 받습니다.</p>
<p>과정:</p>
<p>벡터가 내부적으로 관리하는 메모리 공간에서 직접 생성자를 호출함.</p>
<p>복사나 이동 과정이 아예 발생하지 않음 (완벽한 최적화).</p>
<h3 id="왜-emplace_back이-더-좋은가요">왜 emplace_back이 더 좋은가요?</h3>
<p><strong>성능 최적화:</strong> 불필요한 복사 생성자와 소멸자 호출을 방지합니다. 특히 객체의 크기가 크거나 스레드 풀의 Task처럼 이동이 복잡한 경우 차이가 큽니다.</p>
<p><strong>가변 인자 템플릿</strong>: emplace_back은 우리가 스레드 풀에서 봤던 것처럼 Args&amp;&amp;...를 사용하기 때문에, 생성자의 인자가 몇 개든 상관없이 대응할 수 있습니다.</p>
<h3 id="항상-emplace_back이-정답일까">항상 emplace_back이 정답일까?</h3>
<p>무조건 emplace_back이 좋은 것은 아닙니다.</p>
<p>*<em>가독성: *</em>이미 만들어진 객체 변수가 있다면 push_back(std::move(obj))를 쓰는 게 의도가 더 명확할 수 있습니다.</p>
<p><strong>explicit 생성자:</strong> explicit 키워드가 붙은 생성자는 push_back에서 암시적 변환이 안 되어 에러를 잡아주지만, emplace_back은 이를 무시하고 강제로 생성해 버릴 수 있어 예기치 못한 타입 변환이 일어날 수 있습니다.</p>
<h2 id="lock_guard-unlock-시점">lock_guard unlock 시점</h2>
<p>lock_guard나 unique_lock 같은 RAII 객체들은 <strong>파괴되는 시점(소멸자 호출)</strong>에 락을 풀기 때문에, 개발자가 의도적으로 중괄호({ })를 사용해 락의 수명을 강제로 조절하는 기법을 아주 많이 씁니다.</p>
<p>이를 전문 용어로 &quot;Scope-based Locking&quot; 또는 <strong>&quot;Lock Granularity(락의 세밀도) 조절&quot;</strong>이라고 부릅니다.</p>
<h3 id="왜-이렇게-범위를-쪼개야-하나요">왜 이렇게 범위를 쪼개야 하나요?</h3>
<p>만약 중괄호가 없다면 어떻게 될까요?</p>
<p>성능 저하: 만약 task() 실행이 10초 걸린다면, 그 10초 동안 락이 계속 잡혀 있게 됩니다.</p>
<p>병목 현상: 다른 스레드들은 큐에서 일감을 꺼내오고 싶어도 락 때문에 아무것도 못 하고 10초 동안 줄줄이 대기하게 됩니다. 멀티스레드를 쓰는 의미가 사라지죠.</p>
<p>데드락 위험: 락을 오래 잡고 있을수록 다른 락과 꼬여서 프로그램이 멈출 확률이 높아집니다.</p>
<h2 id="람다-캡처-방식">람다 캡처 방식</h2>
<h3 id="1-값-복사-캡처--x">1. 값 복사 캡처 ([=], [x])</h3>
<p>변수의 현재 값을 복사해서 람다 내부로 가져옵니다.</p>
<p>언제 쓰면 좋을까?
<strong>비동기/멀티스레드 작업:</strong> 람다가 실행되는 시점에 원래 변수가 이미 파괴되었을 가능성이 클 때 사용합니다. (가장 안전한 방법)</p>
<p><strong>작은 데이터 타입:</strong> int, float, bool 처럼 복사 비용이 매우 저렴한 경우입니다.</p>
<p><strong>함수형 프로그래밍:</strong> 외부 상태에 영향을 주지 않고 독립적인 계산을 수행하고 싶을 때 적합합니다.</p>
<h4 id="주의점">주의점</h4>
<p>무거운 객체(큰 std::vector 등)를 복사하면 성능 저하가 발생합니다.</p>
<p>람다 내부에서 값을 수정하려면 mutable 키워드가 필요합니다.</p>
<h3 id="2-참조-캡처--x">2. 참조 캡처 ([&amp;], [&amp;x])</h3>
<p>변수의 주소(참조)를 가져와 원본에 직접 접근합니다.</p>
<p>언제 쓰면 좋을까?
<strong>즉시 실행되는 함수:</strong> std::sort, std::for_each 처럼 해당 라인에서 바로 실행되고 끝나는 알고리즘 함수에 넘길 때 최적입니다.</p>
<p><strong>대용량 객체:</strong> 복사 비용이 큰 객체를 다룰 때 성능을 위해 사용합니다.</p>
<p><strong>원본 수정 필요:</strong> 람다 내부에서 외부 변수의 값을 직접 바꿔야 할 때 사용합니다.</p>
<h4 id="주의점-매우-중요">주의점 (매우 중요)</h4>
<p>Dangling Reference(허공의 참조): 비동기 작업(std::thread 등)에서 참조 캡처를 썼는데, 람다가 실행되기도 전에 원본 변수가 스코프를 벗어나 사라지면 프로그램이 Crash 납니다.</p>
<h3 id="이동-캡처-c14-이상">이동 캡처 (C++14 이상)</h3>
<p>복사하기엔 너무 무겁고, 참조하기엔 수명이 걱정될 때 쓰는 이동(Move) 캡처입니다. 우리가 스레드 풀에서 packaged_task를 다룰 때와 같은 원리입니다.</p>
<h2 id="stdresult_offargstype">std::result_of&lt;F(Args...)&gt;::type</h2>
<p><strong>&quot;함수 F에 인자 Args...를 넣고 호출했을 때 나올 최종 결과물의 타입&quot;</strong>이 무엇인지 컴파일러에게 물어봐서 알아낸 그 타입 자체를 반환합니다.</p>
<h4 id="왜-fargs-형태인가요">왜 F(Args...) 형태인가요?</h4>
<p>여기서 F(Args...)는 실제 함수 호출이 아니라, <strong>함수 서명(Signature)</strong>을 흉내 내는 것입니다.</p>
<p>F: 호출 가능한 대상 (함수 포인터, 람다, Functor 등)</p>
<p>Args...: 그 함수에 전달할 인자들의 타입들</p>
<h4 id="현대-c에서의-변화">현대 C++에서의 변화</h4>
<p>std::result_of는 C++11부터 쓰였지만, C++17부터는 std::invoke_result로 대체되었습니다. 기능은 같지만 이름이 훨씬 명확해졌고 사용법도 살짝 바뀌었습니다.</p>
<pre><code class="language-cpp">
C++11/14: std::result_of&lt;F(Args...)&gt;::type

C++17/20: std::invoke_result&lt;F, Args...&gt;::type </code></pre>
<h2 id="packaged_task">Packaged_task</h2>
<p>future와 packaged_task는 <strong>동일한 내부 메모리(Shared State)</strong>를 공유하는 짝꿍입니다.</p>
<p>packaged_task는 쓰기 전용 포인터를 가짐.</p>
<p>future는 읽기 전용 포인터를 가짐.</p>
<h2 id="threadpool-enqueue_job에서-해당-스코프를-벗어나도-실행이-가능한-이유shared_ptr">ThreadPool enqueue_job에서 해당 스코프를 벗어나도 실행이 가능한 이유(shared_ptr)</h2>
<h4 id="과거-방식-일반-포인터-">과거 방식: 일반 포인터 (*)</h4>
<p>일반 포인터는 메모리 주소만 들고 있을 뿐, <strong>&quot;누가 나를 쓰고 있는지&quot;</strong>에 관심이 없습니다.</p>
<p>문제점: EnqueueJob 함수가 끝나서 지역 변수가 파괴되어도, 큐에 들어간 람다는 여전히 그 주소만 붙들고 있습니다.</p>
<p>결과: 워커 스레드가 나중에 그 주소를 찾아가면? 이미 OS에 반납된 메모리라 Crash가 납니다.</p>
<p>해결책: 이걸 막으려면 질문하신 것처럼 별도의 <strong>&#39;전역 컨테이너&#39;</strong>나 <strong>&#39;메모리 풀&#39;</strong>을 만들어서, 모든 작업이 끝날 때까지 수동으로 메모리를 관리하고 하나씩 지워줘야 했습니다. (관리가 지옥 같았죠.)</p>
<h4 id="현대-방식-shared_ptr--람다-캡처">현대 방식: shared_ptr + 람다 캡처</h4>
<p>shared_ptr는 <strong>&quot;참조 횟수(Reference Count)&quot;</strong>라는 장부를 내장하고 있습니다.</p>
<p>혁신: 람다가 [task]를 통해 shared_ptr를 복사해서 큐로 들어가는 순간, 이 장부의 숫자가 올라갑니다.</p>
<p>자동화: EnqueueJob 함수가 끝나도 큐 안의 람다가 여전히 1점을 쥐고 있기 때문에, 별도의 컨테이너 없이도 메모리가 스스로 살아남습니다.</p>
<p>정리: 작업이 끝나고 람다가 소멸하면 장부가 0이 되고, 그제야 메모리가 스스로 사라집니다.</p>
<h2 id="move-vs-forward">move vs forward</h2>
<p><img src="https://velog.velcdn.com/images/creat_my_world/post/cdb44518-0c0a-437f-b87f-6cfe3e178df5/image.png" alt=""></p>
<h2 id="좌측값-우측값-메모리-위치">좌측값, 우측값 메모리 위치</h2>
<p><img src="https://velog.velcdn.com/images/creat_my_world/post/84b1457b-e0ce-42d2-abcf-d79d914d5e44/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[atomic & 비동기]]></title>
            <link>https://velog.io/@creat_my_world/atomic-%EB%B9%84%EB%8F%99%EA%B8%B0-ai-%EC%9D%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@creat_my_world/atomic-%EB%B9%84%EB%8F%99%EA%B8%B0-ai-%EC%9D%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Thu, 12 Mar 2026 08:06:11 GMT</pubDate>
            <description><![CDATA[<h1 id="c-멀티스레딩-및-비동기">C++ 멀티스레딩 및 비동기</h1>
<p>현대  C++의 동기화 도구들은 <strong>&quot;안전성, 성능, 추상화&quot;</strong>라는 세 가지 축을 중심으로 발전해 왔습니다.</p>
<h3 id="1-저수준의-제왕-stdatomic--memory_order">1. 저수준의 제왕: std::atomic &amp; memory_order</h3>
<blockquote>
<p>일반적인 mutex는 운영체제의 커널 리소스를 사용하므로 매우 무겁습니다. 스레드가 잠들고 깨어날 때 발생하는 Context Switch 비용을 줄이고, CPU 하드웨어가 제공하는 원자적 명령어를 직접 활용하기 위해 등장했습니다.</p>
</blockquote>
<h4 id="좋은-점극강의-성능-락lock-없이-데이터-오염을-막을-수-있습니다-lock-free">좋은 점극강의 성능: 락(Lock) 없이 데이터 오염을 막을 수 있습니다 (Lock-free).</h4>
<h4 id="미세-조정-memory_order를-통해-하드웨어-수준의-명령어-재배치를-제어할-수-있습니다">미세 조정: memory_order를 통해 하드웨어 수준의 명령어 재배치를 제어할 수 있습니다.</h4>
<h4 id="추천-상황공유-카운터-통계-수치-방문자-수-계산플래그flag-스레드-종료-신호-전달성능이-극도로-중요한-자료구조-lock-free-큐-스택-구현">추천 상황공유 카운터: 통계 수치, 방문자 수 계산.플래그(Flag): 스레드 종료 신호 전달.성능이 극도로 중요한 자료구조: Lock-free 큐, 스택 구현.</h4>
<h4 id="⚠️-주의-복잡한-로직을-아토믹으로만-짜는-것은-자살-행위와-같습니다-버그-찾기가-불가능에-가까움">⚠️ 주의: 복잡한 로직을 아토믹으로만 짜는 것은 자살 행위와 같습니다. (버그 찾기가 불가능에 가까움)</h4>
<h3 id="2-전통의-강자-stdmutex--stdcondition_variable">2. 전통의 강자: std::mutex &amp; std::condition_variable</h3>
<blockquote>
<p>여러 줄의 코드(임계 영역)를 하나의 원자적 단위로 묶어야 할 때, 그리고 스레드 간의 <strong>&quot;신호(Signal)&quot;</strong>를 주고받아야 할 때 필요합니다. 아토믹만으로는 &quot;데이터가 준비될 때까지 잠들기&quot;를 구현하기 어렵기 때문입니다.</p>
</blockquote>
<h4 id="좋은-점범용성-가장-직관적이고-안전하게-데이터-레이스를-막을-수-있습니다">좋은 점범용성: 가장 직관적이고 안전하게 데이터 레이스를 막을 수 있습니다.</h4>
<h4 id="자원-효율-condition_variable은-조건이-맞지-않으면-스레드를-잠재워-cpu-점유율을-0으로-만듭니다">자원 효율: condition_variable은 조건이 맞지 않으면 스레드를 잠재워 CPU 점유율을 0으로 만듭니다.</h4>
<p> 추천 상황복잡한 공유 자원 보호: 여러 변수를 한꺼번에 수정해야 할 때.생산자-소비자 패턴: 큐에 데이터가 들어올 때까지 기다려야 하는 상황.</p>
<h3 id="3-비동기의-추상화-future-promise-packaged_task">3. 비동기의 추상화: future, promise, packaged_task</h3>
<blockquote>
<p>이들은 <strong>&quot;함수의 결과값&quot;</strong>을 어떻게 안전하게 전달할 것인가에 대한 고민에서 나왔습니다.</p>
</blockquote>
<h3 id="a-stdpromise--stdfuture-수동-배달">A. std::promise &amp; std::future (수동 배달)</h3>
<p> 왜? 결과가 언제 나올지 모르는 상황에서 결과값을 담을 <strong>&#39;우체통&#39;</strong>이 필요해서.상황: 네트워크 응답 대기, 저수준 스레드 관리.</p>
<h3 id="b-stdpackaged_task-반자동-공장">B. std::packaged_task (반자동 공장)</h3>
<p> 왜? 함수 실행과 결과 전달(promise)을 하나로 묶어 코드 중복을 줄이려고.상황: 스레드 풀(Thread Pool) 구현. 일감을 큐에 넣고 결과만 나중에 챙길 때.</p>
<h3 id="c-stdasync-완전-자동-배달">C. std::async (완전 자동 배달)</h3>
<p> 왜? 스레드 생성, 관리, 결과 전달을 한 줄로 끝내고 싶어서.상황: 독립적인 계산 작업, 간단한 I/O 분리. 대부분의 일반적인 비동기 작업.</p>
<h2 id="4-상황별-도구-선택-가이드">4. 상황별 도구 선택 가이드</h2>
<h4 id="1-단순히-숫자-하나만-안전하게-올리고-싶다stdatomicmutex보다-훨씬-빠름여러">1. 단순히 숫자 하나만 안전하게 올리고 싶다std::atomicMutex보다 훨씬 빠름여러</h4>
<h4 id="2-스레드가-하나의-큐를-공유한다mutex--condition_variable가장-표준적이고-안전한-방식">2. 스레드가 하나의 큐를 공유한다mutex + condition_variable가장 표준적이고 안전한 방식</h4>
<h4 id="3-함수를-별도-스레드에서-돌리고-결과만-받고-싶다stdasync가장-쉽고-관리가-필요-없음">3. 함수를 별도 스레드에서 돌리고 결과만 받고 싶다std::async가장 쉽고 관리가 필요 없음</h4>
<h4 id="4-스레드-풀을-직접-구현하고-싶다stdpackaged_task함수와-결과를-묶어-관리하기">4. 스레드 풀을 직접 구현하고 싶다std::packaged_task함수와 결과를 묶어 관리하기</h4>
<h4 id="5-최적이벤트가-발생하면-모든-스레드를-동시에-깨우고-싶다-shared_future1n-브로드캐스트가-가능함">5. 최적이벤트가 발생하면 모든 스레드를 동시에 깨우고 싶다 shared_future1:N 브로드캐스트가 가능함</h4>
<h2 id="5-핵심-요약">5. 핵심 요약</h2>
<p><strong>복사보다 이동(std::move)</strong>: 현대 C++ 비동기 객체들은 소유권의 유일성을 위해 복사를 금지합니다.</p>
<p><strong>고수준 우선</strong>: 항상 가장 높은 추상화 수준(async)부터 고려하고, 성능이나 제어권이 부족할 때 저수준(atomic, promise)으로 내려가세요.</p>
<p><strong>RAII 활용</strong>: 쌩 락(m.lock()) 대신 std::lock_guard나 std::unique_lock을 사용하여 예외 안전성을 확보하세요.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[move, forwarding]]></title>
            <link>https://velog.io/@creat_my_world/move-forwarding</link>
            <guid>https://velog.io/@creat_my_world/move-forwarding</guid>
            <pubDate>Wed, 19 Nov 2025 15:19:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-stdmove란-뭐고-진짜로-뭘-하는가">1. std::move란 뭐고, 진짜로 뭘 하는가?</h2>
<h3 id="11-stdmove의-정체">1.1. std::move의 정체</h3>
<pre><code class="language-cpp">#include &lt;utility&gt;

std::move(x);</code></pre>
<pre><code class="language-cpp">template &lt;typename T&gt;
constexpr typename std::remove_reference&lt;T&gt;::type&amp;&amp; move(T&amp;&amp; x) noexcept {
    return static_cast&lt;typename std::remove_reference&lt;T&gt;::type&amp;&amp;&gt;(x);
} //실제 구현
</code></pre>
<p><strong>std::move는 단지 x를 T&amp;&amp;로 static_cast 해주는 함수</strong>
→ <strong>“이 값은 이제 rvalue(xvalue)처럼 취급해도 좋다”는 의사를 알리는 역할</strong></p>
<p>메모리 복사 X
자원 이동 X
내부 상태 변경 X</p>
<p>→ 진짜 “move” 작업은 move constructor / move assignment 안에서 직접 구현해야 한다</p>
<h3 id="12-stdmove-사용-예시">1.2. std::move 사용 예시</h3>
<pre><code class="language-cpp">struct Buffer {
    int* data;
    std::size_t size;

    Buffer(std::size_t n)
        : data(new int[n]), size(n) {}

    ~Buffer() {
        delete[] data;
    }

    // copy constructor
    Buffer(const Buffer&amp; other)
        : data(new int[other.size]), size(other.size) {
        std::copy(other.data, other.data + size, data);
    }

    // move constructor
    Buffer(Buffer&amp;&amp; other) noexcept
        : data(other.data), size(other.size) {
        other.data = nullptr;  // 원본 비우기
        other.size = 0;
    }
};

int main() {
    Buffer a(1000);
    Buffer b = std::move(a);  // 여기서 move ctor 호출

    // std::move는 a를 rvalue로 캐스팅만 해줌
    // 진짜 포인터 옮기기 / other 비우기는 Buffer(Buffer&amp;&amp;) 안에서 일어남
    //이제 a는  moved-from 객체이다.
    //moved-from 객체는 “valid but unspecified state” 여야 한다. 
}</code></pre>
<h2 id="2-universal-reference-forwarding-reference">2. Universal Reference (Forwarding Reference)</h2>
<h3 id="21-보편적-레퍼런스">2.1. 보편적 레퍼런스</h3>
<p>Scott Meyers가 Effective Modern C++에서 universal reference라고 부른 개념이고,
C++ 표준 용어로는 <strong>forwarding reference</strong>라고 부른다.</p>
<p><strong>템플릿 파라미터 T에 대해 T&amp;&amp; 형태로,
그리고 T가 타입 추론(deduction)되는 위치에 있는 레퍼런스</strong></p>
<p>함수 템플릿의 파라미터가 T&amp;&amp;이고,
호출 인자를 보고 T를 추론하는 그런 자리의 레퍼런스 → forwarding reference</p>
<p>ex) 대부분 클래스 템플릿은 vector&lt;int.&gt;식으로 타입이 명시되기 때문에(추론 x) T&amp;&amp;가 rvalue reference이다.</p>
<p>예:</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void f(T&amp;&amp; x);   // 여기서 x는 forwarding reference (universal reference)</code></pre>
<p>여기서 x는 상황에 따라 자동으로 lvalue reference 또는 rvalue reference가 된다.</p>
<h3 id="211-어떻게-동작하는지-예시">2.1.1. 어떻게 동작하는지 예시</h3>
<pre><code class="language-cpp">void test() {
    int a = 10;
    const int ca = 20;

    f(a);          // T = int&amp;      -&gt; 매개변수 타입: int&amp; &amp;&amp;  -&gt; int&amp;
    f(ca);         // T = const int&amp;-&gt; 매개변수 타입: const int&amp; &amp;&amp; -&gt; const int&amp;
    f(10);         // T = int       -&gt; 매개변수 타입: int&amp;&amp;
}</code></pre>
<p>lvalue를 넘기면 → T가 int&amp;, const int&amp; 이런 식으로 deduce</p>
<p>rvalue를 넘기면 → T가 그냥 int, std::string 같은 값 타입으로 deduce</p>
<p>그 다음 T&amp;&amp;에 *<em>reference collapsing *</em>규칙이 적용됨.</p>
<h3 id="22-reference-collapsing-rule-레퍼런스-겹침-규칙">2.2. Reference Collapsing Rule (레퍼런스 겹침 규칙)</h3>
<p>C++에서는 &amp;와 &amp;&amp;가 섞여서 겹칠 때(특히 템플릿에서) 어떤 reference 타입이 되는지에 대한 규칙이 필요하다.</p>
<p>규칙은 딱 하나 테이블로 기억하면 됨:</p>
<p>조합    ---&gt; 결과
T&amp; &amp; ---&gt;    T&amp;
T&amp; &amp;&amp; ---&gt;    T&amp;
T&amp;&amp; &amp; ---&gt;    T&amp;
T&amp;&amp; &amp;&amp; ---&gt;    T&amp;&amp;</p>
<p>즉,</p>
<p>“한 번이라도 &amp;가 끼면 결국 &amp;가 된다” &amp; = 1, &amp;&amp; = 0의 OR 연산이라고 생각하면된다.
(&amp;&amp; &amp;&amp;인 경우만 진짜 &amp;&amp; 유지)</p>
<p>예를 들어:</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void f(T&amp;&amp; x);

int main() {
    int i = 0;
    f(i);
}</code></pre>
<p>여기서:</p>
<p>i는 lvalue → T = int&amp;</p>
<p>매개변수 타입: T&amp;&amp; → int&amp; &amp;&amp; → reference collapsing → int&amp;</p>
<p>그래서 결국 x는 int&amp;가 됨 (lvalue reference)</p>
<p>반면 rvalue인 f(0);은:</p>
<p>T = int</p>
<p>매개변수 타입 int&amp;&amp; &amp;&amp; → int&amp;&amp;</p>
<p>진짜 rvalue reference로 받게 됨.</p>
<p><strong>T&amp;&amp;가 상황에 따라 lvalue도, rvalue도 바인딩할 수 있어서 universal/forwarding reference라고 부르는 이유이다</strong></p>
<h2 id="3-perfect-forwarding과-stdforward">3. Perfect Forwarding과 std::forward</h2>
<h3 id="31-왜-perfect-forwarding이-필요한가">3.1. 왜 Perfect Forwarding이 필요한가?</h3>
<p>문제 상황을 먼저 보자:</p>
<pre><code class="language-cpp">void foo(const std::string&amp; s) { /* lvalue 전용 */ }
void foo(std::string&amp;&amp; s)      { /* rvalue 전용 */ }

template &lt;typename T&gt;
void wrapper(T x) {
    foo(x);   // ??? 어떤 foo가 불릴까?
}
</code></pre>
<p>여기서 wrapper를 이렇게 쓰면:</p>
<pre><code class="language-cpp">std::string s = &quot;hi&quot;;
wrapper(s);         // foo(const std::string&amp;) 기대
wrapper(std::string(&quot;hi&quot;)); // 원래는 foo(std::string&amp;&amp;) 기대</code></pre>
<p>하지만 wrapper 안에서 x는 항상 lvalue이다. (x는 이름 있는 변수이기 때문에)
그래서 두 경우 모두 foo(const std::string&amp;)이 호출된다.</p>
<p>“원래 인자가 lvalue면 lvalue로, rvalue면 rvalue로 그대로 전달하고 싶지만
wrapper 때문에 다 lvalue가 되어버림 → 이걸 해결하려는 기법이 perfect forwarding”</p>
<h3 id="32-forwarding-reference--stdforward">3.2. Forwarding Reference + std::forward</h3>
<p>perfect forwarding을 위해 필요한 두 가지:</p>
<p>매개변수 타입: T&amp;&amp; (forwarding reference)</p>
<p>인자 전달: std::forward<T>(arg)</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void wrapper(T&amp;&amp; x) {
    foo(std::forward&lt;T&gt;(x));
}</code></pre>
<h3 id="321-stdforward의-정체">3.2.1. std::forward의 정체</h3>
<p>개념적으로는:</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
T&amp;&amp; forward(typename std::remove_reference&lt;T&gt;::type&amp; x) {
    return static_cast&lt;T&amp;&amp;&gt;(x);
}
</code></pre>
<p>T가 lvalue reference인 경우 (T = U&amp;):</p>
<p>std::forward<T>(x) → static_cast&lt;U&amp;&gt;(x) → lvalue로 캐스팅</p>
<p>T가 값 타입인 경우 (T = U):</p>
<p>std::forward<T>(x) → static_cast&lt;U&amp;&amp;&gt;(x) → rvalue로 캐스팅</p>
<p>즉,</p>
<p>“T가 어떻게 deduce되었는지에 따라
x를 lvalue 또는 rvalue로 되돌려 보내는 캐스팅 함수”</p>
<p>그래서:</p>
<pre><code class="language-cpp">std::string s = &quot;hi&quot;;

wrapper(s);
// T = std::string&amp;  → std::forward&lt;T&gt;(x) == lvalue → foo(const std::string&amp;)

wrapper(std::string(&quot;hi&quot;));
// T = std::string   → std::forward&lt;T&gt;(x) == rvalue → foo(std::string&amp;&amp;)
</code></pre>
<p>→ 원래 인자의 value category(lvalue/rvalue)를 그대로 보존해서 forward하는 것
→ 그래서 perfect forwarding 이라고 부름.</p>
<h3 id="33-perfect-forwarding-패턴-템플릿">3.3. Perfect Forwarding 패턴 템플릿</h3>
<p>가장 흔한 패턴:</p>
<pre><code class="language-cpp">template &lt;typename F, typename... Args&gt;
decltype(auto) call_with_log(F&amp;&amp; f, Args&amp;&amp;... args) {
    std::cout &lt;&lt; &quot;calling...\n&quot;;
    return std::forward&lt;F&gt;(f)(
        std::forward&lt;Args&gt;(args)...   // 각각의 l/rvalue 성질 그대로 전달
    );
}</code></pre>
<p>함수 객체 f와 인자 args...를 받아서</p>
<p>로깅을 찍고</p>
<p>원래대로 f(args...)를 호출하되,</p>
<p>각 인자가 lvalue인지 rvalue인지 그대로 유지해서 전달</p>
<p><em>*std::function, 래퍼, 데코레이터, 이벤트 시스템, 커스텀 make_</em> 함수 등에서 많이 나오는 패턴이다
**</p>
<h3 id="4-stdmove-vs-stdforward-차이-정리">4. std::move vs std::forward 차이 정리</h3>
<p>함수    하는 일    언제 쓰나
<strong>std::move(x)</strong>    x를 무조건 rvalue(xvalue)로 캐스팅    “이 자원은 이제 이쪽으로 완전히 넘길게”
<strong>std::forward<T>(x)</strong>    T에 따라 lvalue 또는 rvalue로 조건부 캐스팅    템플릿에서 원래 인자의 value category 유지하여 전달</p>
<p><strong>일반 코드(템플릿 아니거나, 항상 move하고 싶은 곳)에서는 → std::move</strong></p>
<p><strong>템플릿 래퍼, 팩토리, wrapper 함수에서 “원래 인자 상태 그대로 전달” 하고 싶으면 → std::forward + forwarding reference</strong></p>
<h2 id="5-사용-예시-모음">5. 사용 예시 모음</h2>
<h3 id="51-my_swap-구현">5.1. my_swap 구현</h3>
<pre><code class="language-cpp">template &lt;typename T&gt;
void my_swap(T&amp; a, T&amp; b) {
    T temp(std::move(a)); // a의 자원 통째로 temp로 이동
    a = std::move(b);     // b의 자원을 a로 이동
    b = std::move(temp);  // temp의 자원을 b로 이동
}</code></pre>
<p>여기서는 a, b, temp가 전부 정확히 어떤 객체를 가리키는지 명확하고</p>
<p>“무조건 자원을 이동해도 된다”는 의도가 분명하므로 → std::move가 맞다</p>
<h3 id="52-factory-함수에서-perfect-forwarding">5.2. Factory 함수에서 perfect forwarding</h3>
<pre><code class="language-cpp">template &lt;typename T, typename... Args&gt;
std::unique_ptr&lt;T&gt; make_my_unique(Args&amp;&amp;... args) {
    return std::unique_ptr&lt;T&gt;(
        new T(std::forward&lt;Args&gt;(args)...)
    );
}</code></pre>
<p>std::make_unique랑 같은 패턴</p>
<p>인자가 lvalue면 lvalue 그대로, rvalue면 rvalue 그대로 T 생성자에 전달</p>
<h3 id="53-wrapper--decorator-함수">5.3. Wrapper / Decorator 함수</h3>
<pre><code class="language-cpp">template &lt;typename Func, typename... Args&gt;
decltype(auto) call_with_retry(Func&amp;&amp; f, Args&amp;&amp;... args) {
    for (int i = 0; i &lt; 3; ++i) {
        try {
            return std::forward&lt;Func&gt;(f)(
                std::forward&lt;Args&gt;(args)...
            );
        } catch (...) {
            std::cout &lt;&lt; &quot;retry &quot; &lt;&lt; i &lt;&lt; &quot;\n&quot;;
        }
    }
    throw std::runtime_error(&quot;failed after 3 retries&quot;);
}
</code></pre>
<p>f가 함수 포인터든, 람다든 상관없이 받아서</p>
<p>args... 역시 lvalue/rvalue 그대로 유지해서 넘길 수 있음</p>
<blockquote>
<p>** &lt;결론&gt;**
  T&amp;&amp; (forwarding reference) + reference collapsing 규칙 + std::forward<T> 조합 덕분에,
템플릿 함수는 인자의 lvalue/rvalue 성질을 그대로 유지해서 다른 함수로 전달할 수 있다.</p>
</blockquote>
<p>만약 이 패턴이 없다면,
lvalue / rvalue / const / non-const 조합마다 별도의 오버로드를 전부 작성해야 해서
wrapper / decorator / factory / std::make_unique, emplace 같은 함수들은
사실상 구현 불가능에 가까운 수준으로 복잡해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[  
Value Category (prvalue / xvalue / lvalue / glvalue / rvalue)
  ]]></title>
            <link>https://velog.io/@creat_my_world/Value-Category-prvalue-xvalue-lvalue-glvalue-rvalue</link>
            <guid>https://velog.io/@creat_my_world/Value-Category-prvalue-xvalue-lvalue-glvalue-rvalue</guid>
            <pubDate>Wed, 19 Nov 2025 14:27:27 GMT</pubDate>
            <description><![CDATA[<h2 id="1-lvalue">1. lvalue</h2>
<h3 id="1-1-정의">1-1. 정의</h3>
<p>“이름이 있고, 주소를 잡을 수 있고, 계속 존재하는 객체”를 나타내는 표현식</p>
<p>&amp;expr 해서 주소를 얻을 수 있는 애들이 대부분 lvalue</p>
<h3 id="1-2-예시">1-2. 예시</h3>
<pre><code class="language-cpp">int x = 10;
x;          // x  → lvalue
int&amp; r = x; // r  → lvalue (참조도 결국 객체의 또 다른 이름)

struct S { int a; };
S s;
s;          // s  → lvalue
s.a;        // s.a도 lvalue</code></pre>
<p>x, s, s.a 전부 “실제 메모리 어딘가에 있는 객체”</p>
<p>나중에 다시 읽고/쓰고 싶을 때 재사용 가능한 애들 → lvalue</p>
<h2 id="2-prvalue-pure-rvalue">2. prvalue (pure rvalue)</h2>
<h3 id="2-1-정의">2-1. 정의</h3>
<p>“계산 결과로 나오는 값 자체” / 주로 임시 객체, 리터럴 같은 것</p>
<p>예전 C++에서 말하던 “rvalue”에 제일 가까움</p>
<p>보통 새 객체를 만들거나, 값만 있다가 사라지는 애들</p>
<h3 id="2-2-예시">2-2. 예시</h3>
<pre><code class="language-cpp">10;          // int literal → prvalue
3.14;        // double literal → prvalue
true;        // bool literal → prvalue

int x = 10;
x + 1;       // x + 1 → prvalue

std::string(&quot;abc&quot;);  // 임시 std::string 객체 → prvalue
S();                 // 임시 S 객체 → prvalue</code></pre>
<p>이 값들은 “메모리 어딘가에 이름 붙은 객체”랑 연결되어 있는 느낌이 아니라,</p>
<p>“계산 결과로 잠깐 존재하는 값”에 더 가깝다 → prvalue</p>
<h2 id="3-xvalue-expiring-value">3. xvalue (expiring value)</h2>
<h3 id="3-1-정의">3-1. 정의</h3>
<p><strong>xvalue (expiring value)</strong>는:</p>
<p>“곧 수명이 끝나서 자원을 빼앗아도 되는 객체 표현”</p>
<p>“진짜 객체를 가리키긴 하는데, 더 이상 이걸 원래 주인처럼 쓰지 않을 것” 이라는 의미를 가진 값</p>
<p>대표적으로 rvalue reference로 표현된 객체가 xvalue가 됨.</p>
<h3 id="3-2-예시">3-2. 예시</h3>
<pre><code class="language-cpp">std::string make();

std::string s = &quot;hello&quot;;
std::string&amp;&amp; r = std::move(s);

r;                // r 자체는 lvalue (이름 있는 변수)
std::move(s);     // 이 표현식 → xvalue
std::move(r);     // 이것도 표현식 → xvalue</code></pre>
<p>std::move(s) 는:</p>
<p>“s라는 실제 객체” (메모리 상에 존재)를 가리키면서</p>
<p><strong>“얘는 이제 곧 자원 뺏어가도 된다”</strong>는 의미를 갖는 표현식 → xvalue</p>
<p>즉:</p>
<p>lvalue: “정상적으로 계속 쓸 객체”</p>
<p>xvalue: “아직 존재하긴 하는데, 이제 자원을 뺏어가도 되는 객체”</p>
<p>또 다른 예:</p>
<pre><code class="language-cpp">std::vector&lt;int&gt; v = {1,2,3};
v[0];             // lvalue
std::move(v)[0];  // xvalue (이 표현식이 가리키는 원소)</code></pre>
<h2 id="4-glvalue-generalized-lvalue">4. glvalue (generalized lvalue)</h2>
<h3 id="4-1-정의">4-1. 정의</h3>
<p>glvalue는:</p>
<h4 id="메모리-상의-어떤-객체를-가리키는-값">“메모리 상의 어떤 객체를 가리키는 값”</h4>
<p>즉, lvalue ∪ xvalue</p>
<p>다시 말해:</p>
<p>lvalue : 정상적으로 쓰일 객체</p>
<p>xvalue : 곧 만료(expire)될 객체</p>
<p>둘 다 “실제 객체”를 가리킨다는 점에서 공통됨 → 그걸 묶어서 glvalue라고 부름.</p>
<h3 id="4-2-예시">4-2. 예시</h3>
<p>lvalue 예시: x, s, s.a, v[0]</p>
<p>xvalue 예시: std::move(s), std::move(v)[0]</p>
<p>→ 이걸 전부 glvalue라고 부를 수 있다.</p>
<h2 id="5-rvalue">5. rvalue</h2>
<h3 id="5-1-정의">5-1. 정의</h3>
<p>rvalue는:</p>
<p>“주로 임시 객체, 이동 대상(move 대상)”
즉, prvalue ∪ xvalue</p>
<p>예전 C++에서 “rvalue”라고 부르던 녀석들이</p>
<p>C++11 이후에는 prvalue / xvalue로 나뉘고</p>
<p>그 둘을 묶어서 다시 rvalue라고 부르게 된 것.</p>
<h3 id="5-2-rvalue-예시들">5-2. rvalue 예시들</h3>
<pre><code class="language-cpp">10;                   // prvalue → rvalue
x + 1;                // prvalue → rvalue
S();                  // prvalue → rvalue
std::string(&quot;abc&quot;);   // prvalue → rvalue

std::move(s);         // xvalue → rvalue
std::move(v)[0];      // xvalue → rvalue</code></pre>
<p>정리하면:</p>
<p>prvalue : 값 자체, 새로 만들어지는 임시, 리터럴</p>
<p>xvalue : 실제 객체를 가리키지만 “만료(expiring)된” 상태</p>
<p>이 둘을 합쳐서 → rvalue</p>
<h2 id="6-포함-관계">6. 포함 관계</h2>
<p><strong>glvalue = lvalue ∪ xvalue</strong></p>
<p><strong>rvalue = prvalue ∪ xvalue</strong></p>
<p>즉 xvalue는 glvalue이면서 rvalue에도 포함돼 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[우측값(rvalue)과 이동 생성자]]></title>
            <link>https://velog.io/@creat_my_world/%EC%9A%B0%EC%B8%A1%EA%B0%92rvalue%EA%B3%BC-%EC%9D%B4%EB%8F%99-%EC%83%9D%EC%84%B1%EC%9E%90</link>
            <guid>https://velog.io/@creat_my_world/%EC%9A%B0%EC%B8%A1%EA%B0%92rvalue%EA%B3%BC-%EC%9D%B4%EB%8F%99-%EC%83%9D%EC%84%B1%EC%9E%90</guid>
            <pubDate>Wed, 19 Nov 2025 13:49:49 GMT</pubDate>
            <description><![CDATA[<h2 id="1-copy-elision-복사-생략">1. Copy Elision (복사 생략)</h2>
<h3 id="1-1-copy-elision이란">1-1. Copy elision이란?</h3>
<blockquote>
<p>copy elision은 말 그대로 “복사(또는 이동)를 생략하는 최적화이다.</p>
</blockquote>
<p>copy constructor 또는 move constructor가 호출되어야 하는 지점에서</p>
<p>컴파일러가 임시 객체를 만들지 않고, 최종 목적지에 바로 객체를 생성해서</p>
<p>복사/이동 생성자 호출 자체를 없애 버리는 최적화이다.</p>
<h4 id="예시">예시:</h4>
<p>함수에서 지역 객체를 값으로 return 할 때
→ RVO(Return Value Optimization), NRVO(Named RVO)</p>
<p>임시 객체(prvalue)를 함수 인자로 넘길 때</p>
<p>C++17 이후: prvalue의 “temporary materialization” 자체를 생략하는 규칙이 들어감
→ 특정 상황에서는 copy/move ctor 호출이 아예 없다고 표준에 명시됨 (guaranteed copy elision)</p>
<h3 id="1-2-왜-필요한가">1-2. 왜 필요한가?</h3>
<h4 id="성능">성능</h4>
<p>큰 객체를 복사/이동하면 메모리 할당, 메모리 복사 비용이 큼</p>
<p>copy/move 자체를 없애 버리면 훨씬 더 빠르게 동작</p>
<p>불필요한 함수 호출 제거</p>
<p>constructor 호출 자체도 함수 호출이니까, 이걸 안 부르면 call overhead 감소</p>
<p>표준 차원에서의 보장 (C++17)</p>
<p>“여기서는 copy/move를 부르지 않고 최종 위치에 바로 만들어도 된다”를 넘어서</p>
<p><strong>“반드시 그렇게 해야 한다”</strong>로 규칙이 강화됨 (guaranteed copy elision)</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

struct MyString {
    MyString() {
        std::cout &lt;&lt; &quot;default ctor\n&quot;;
    }
    MyString(const MyString&amp;) {
        std::cout &lt;&lt; &quot;copy ctor\n&quot;;
    }
    MyString(MyString&amp;&amp;) {
        std::cout &lt;&lt; &quot;move ctor\n&quot;;
    }
};

MyString make() {
    MyString s;       // 여기서 default ctor 한 번 호출
    return s;         // 원래라면 copy 또는 move ctor가 한 번 더 호출될 수 있는 지점
}

int main() {
    MyString x = make();
}</code></pre>
<p>C++17 컴파일러에서 위 코드를 빌드하면, 보통 출력은:</p>
<h4 id="default-ctor">default ctor</h4>
<p>copy ctor / move ctor가 전혀 안 나옴 → copy elision </p>
<p>컴파일러 입장에서는:</p>
<p>make() 안에서 s라는 지역 변수를 따로 만들지 않고,</p>
<p>애초에 main()의 x가 놓일 메모리 위치에 직접 생성해버리는 것처럼 최적화함.</p>
<h2 id="2좌측값lvalue-우측값rvalue">2.좌측값(Lvalue) /우측값(Rvalue)</h2>
<h3 id="2-1-lvalue--rvalue-개념">2-1. Lvalue / Rvalue 개념</h3>
<h4 id="좌측값-lvalue-left-value">좌측값 lvalue (left value)</h4>
<ol>
<li><p>“이름이 있고, 주소를 잡을 수 있는 것”에 가까운 개념</p>
</li>
<li><p>메모리 상에 지속적으로 존재하는 객체를 가리키는 표현식</p>
</li>
<li><p>&amp;obj처럼 주소를 얻을 수 있음</p>
</li>
<li><p>대입문의 왼쪽/오른쪽에 모두 올 수 있음</p>
</li>
</ol>
<h4 id="우측값-rvalue-right-value">우측값 rvalue (right value)</h4>
<ol>
<li><p>“임시 값, 계산 결과로만 잠깐 존재하는 값”</p>
</li>
<li><p>보통 다시 사용할 이름이 없음</p>
</li>
<li><p>대입문의 오른쪽에만 오는 값</p>
</li>
</ol>
<p>간단한 예:</p>
<pre><code class="language-cpp">int x = 10;    // x는 lvalue, 10은 rvalue
int y = x;     // x는 lvalue, (x의 값) 10은 rvalue
x = x + 1;     // x는 lvalue, (x + 1)의 결과는 rvalue</code></pre>
<p>x : 이름이 있고, 주소도 있고, 나중에 또 접근 가능 → lvalue</p>
<p>10, x + 1 : “값 그 자체” → rvalue</p>
<h3 id="2-2-reference와-lvalue--rvalue">2-2. Reference와 Lvalue / Rvalue</h3>
<p>C++11 이전엔 lvalue reference만 있었음:</p>
<pre><code class="language-cpp">int a = 10;
int&amp; ref = a;    // OK, lvalue reference
int&amp; ref2 = 10;  // ERROR, rvalue(10)에는 바인딩 불가</code></pre>
<p>C++11에서 <strong>rvalue reference (T&amp;&amp;)</strong>가 추가됨:</p>
<pre><code class="language-cpp">int&amp;&amp; r = 10;   // OK, rvalue reference
int&amp;&amp; r2 = a;   // ERROR, a는 lvalue → rvalue ref에 못 바인딩</code></pre>
<p>이걸 통해:</p>
<p>“이 값은 더 이상 안 쓸 테니, 자원 뺏어가도 된다”는 의미를 컴파일러에게 전달하고</p>
<p>그걸 기반으로 move semantics가 도입됨.</p>
<h3 id="2-3-rvalue-reference와-overload-예시">2-3. Rvalue reference와 Overload 예시</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void foo(int&amp;  x) { std::cout &lt;&lt; &quot;lvalue ref\n&quot;; }
void foo(int&amp;&amp; x) { std::cout &lt;&lt; &quot;rvalue ref\n&quot;; }

int main() {
    int a = 10;
    foo(a);    // lvalue → foo(int&amp;) 호출
    foo(20);   // rvalue → foo(int&amp;&amp;) 호출
    foo(a + 1); // rvalue → foo(int&amp;&amp;) 호출
}
</code></pre>
<p>lvalue → “복사(copy) 기반”으로 다루고,</p>
<p>rvalue → “이동(move) 기반”으로 다룰 수 있게 됨.</p>
<h4 id="예외적으로-좌측-레퍼런스-는-const-t-일-경우-우측값레퍼런스를-받을-수-있다-const-레퍼런스이기-때문에-임시로-존재하는-객체의-값을-참조만하고-변경할-수-없기-때문이다">예외적으로 좌측 레퍼런스 &amp;는 const T&amp; 일 경우 우측값레퍼런스를 받을 수 있다. const 레퍼런스이기 때문에 임시로 존재하는 객체의 값을 참조만하고 변경할 수 없기 때문이다.</h4>
<h3 id="2-4-move-semantics-stdmove-move-constructor의-도입-이유">2-4. Move semantics, std::move, move constructor의 도입 이유</h3>
<p>C++11에서 move semantics가 도입된 핵심 이유는:</p>
<p>이미 있는 메모리를 그대로 재사용하고 싶은데,</p>
<p>C++98 스타일의 copy semantics로는 항상 “복사”를 해야 했기 때문이다.</p>
<p>대표 예: std::vector, std::string 같이 내부에 큰 heap 메모리를 들고 있는 타입.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

struct Buffer {
    std::vector&lt;int&gt; data;

    Buffer(size_t n) : data(n) {}

    // copy constructor
    Buffer(const Buffer&amp; other) : data(other.data) {
        std::cout &lt;&lt; &quot;copy ctor\n&quot;;
    }

    // move constructor
    Buffer(Buffer&amp;&amp; other) noexcept : data(std::move(other.data)) {
        std::cout &lt;&lt; &quot;move ctor\n&quot;;
    }
};

int main() {
    Buffer a(1000);           // 큰 버퍼
    Buffer b = a;             // copy ctor: data 전체 복사
    Buffer c = std::move(a);  // move ctor: a.data의 자원을 c.data로 &#39;옮김&#39;
}</code></pre>
<p>Buffer b = a;</p>
<p>a는 lvalue → Buffer(const Buffer&amp;) 호출</p>
<p>a.data 내용을 통째로 복사</p>
<p>Buffer c = std::move(a);</p>
<p>std::move(a)는 a를 rvalue로 캐스팅</p>
<p>Buffer(Buffer&amp;&amp;)가 호출되고</p>
<p>내부적으로 c.data는 a.data의 소유권을 가져오고, a.data는 빈 상태로 만듦</p>
<p>여기서 핵심:</p>
<p>std::move는 진짜 “move”를 하지 않고,</p>
<p>“이 객체를 rvalue처럼 취급해라”라고 캐스팅만 해준다.</p>
<p>진짜 move 작업은 move constructor / move assignment operator 안에서 구현하는 것.</p>
<h2 id="3-왜-move-constructor에-noexcept를-붙여야-할까">3. 왜 Move Constructor에 noexcept를 붙여야 할까?</h2>
<h3 id="3-1-표준-컨테이너의-전략">3-1. 표준 컨테이너의 전략</h3>
<p>std::vector, std::string 같은 표준 컨테이너는 내부에서 재할당(reallocate)을 할 때 다음과 같다.</p>
<h4 id="타입-t의-move-constructor가-noexcept-이면">타입 T의 move constructor가 noexcept 이면:</h4>
<p>요소를 옮길 때 move를 사용해도 예외가 안 난다고 믿을 수 있음</p>
<p>→ 빠르고, 불필요한 copy를 안 해도 됨</p>
<h4 id="만약-t의-move-constructor가-noexcept가-아니면">만약 T의 move constructor가 noexcept가 아니면:</h4>
<p>move 도중 예외가 나면, 컨테이너의 내부 상태가 중간에 애매하게 깨질 수 있음</p>
<p>strong exception guarantee를 지키기 어렵기 때문에</p>
<p>차라리 copy constructor를 쓰거나,
move를 사용하지 않는 방향을 선택하기도 한다</p>
<p>즉,</p>
<p>“move가 예외를 던지지 않는 타입” 이면 컨테이너가 더 공격적으로 move를 사용해서 성능 최적화를 할 수 있다</p>
<p>move constructor / move assignment operator 에는
→ 웬만하면 noexcept를 붙이는 게 좋다</p>
<h3 id="3-2-간단-예시">3-2. 간단 예시</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;

struct A {
    A() = default;
    A(const A&amp;) {
        std::cout &lt;&lt; &quot;A copy ctor\n&quot;;
    }
    A(A&amp;&amp;) noexcept {
        std::cout &lt;&lt; &quot;A move ctor (noexcept)\n&quot;;
    }
};

struct B {
    B() = default;
    B(const B&amp;) {
        std::cout &lt;&lt; &quot;B copy ctor\n&quot;;
    }
    B(B&amp;&amp;) {   // noexcept 없음
        std::cout &lt;&lt; &quot;B move ctor (can throw)\n&quot;;
    }
};

int main() {
    std::vector&lt;A&gt; va;
    va.reserve(1);
    va.emplace_back();
    va.push_back(A());  // 재할당 상황을 유도해볼 수 있음 (구체적인 capacity에 따라 다름)

    std::vector&lt;B&gt; vb;
    vb.reserve(1);
    vb.emplace_back();
    vb.push_back(B());
}
</code></pre>
<p>실제 출력은 구현/최적화에 따라 다르지만, 개념적으로:</p>
<p>A의 경우:</p>
<p>A(A&amp;&amp;) noexcept라서
std::vector&lt; A&gt;는 재할당 시 move를 마음껏 사용 가능</p>
<p>B의 경우:</p>
<p>B(B&amp;&amp;)가 noexcept가 아니므로
std::vector<B>는 strong exception guarantee를 지키기 위해
copy를 더 선호하거나, move를 조심스럽게 쓸 수밖에 없다</p>
<h3 id="3-3-언제-noexcept를-붙여도-안전한지">3-3. 언제 noexcept를 붙여도 안전한지</h3>
<p>raw pointer만 이동하는 경우</p>
<p>std::unique_ptr, std::vector, std::string 등,
<strong>자체 move가 이미 noexcept</strong>인 멤버들만 이동하는 경우</p>
<p>예:</p>
<pre><code class="language-cpp">struct MyString {
    char* data;
    std::size_t len;

    MyString(const char* s);
    ~MyString();

    MyString(const MyString&amp; other);            // copy ctor
    MyString&amp; operator=(const MyString&amp; other); // copy assignment

    MyString(MyString&amp;&amp; other) noexcept         // move ctor
        : data(other.data), len(other.len) {
        other.data = nullptr;
        other.len = 0;
    }

    MyString&amp; operator=(MyString&amp;&amp; other) noexcept {
        if (this != &amp;other) {
            delete[] data;
            data = other.data;
            len  = other.len;
            other.data = nullptr;
            other.len  = 0;
        }
        return *this;
    }
};
</code></pre>
<p>여기서 move ctor / move assignment는:</p>
<p>delete[]는 예외를 던지지 않고,</p>
<p>포인터와 숫자만 옮기는 단순 작업이기 때문에</p>
<p>실제로 예외가 발생할 여지가 거의 없음 → noexcept를 붙인다</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[std::fuction]]></title>
            <link>https://velog.io/@creat_my_world/stdfuction</link>
            <guid>https://velog.io/@creat_my_world/stdfuction</guid>
            <pubDate>Thu, 27 Mar 2025 13:20:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>std::function은 단순 함수뿐만 아니라 operator(), 람다, 멤버 함수, std::bind 등을 포함한 모든 callable 객체를 저장하고 호출할 수 있는 다형적인 함수 래퍼 클래스입니다.</p>
</blockquote>
<h2 id="일반적인-함수">일반적인 함수</h2>
<pre><code class="language-cpp">int some_function(const std::string&amp; s)
{
    std::cout &lt;&lt; s &lt;&lt; &quot;함수 호출!&quot; &lt;&lt; std::endl;
    return 0;
}

struct S
{
    void operator()(const std::string&amp; s)
    {
        std::cout &lt;&lt; &quot;구조체 오퍼레이터 함수 호출!&quot; &lt;&lt; std::endl;
    }
};

int main() 
{


    std::function&lt;int(const std::string&amp; s)&gt; f1 = some_function;
    std::function&lt;void(const std::string&amp; s)&gt; f2 = [](const std::string&amp; s) -&gt; void { std::cout &lt;&lt; &quot;람다 함수 호출&quot; &lt;&lt; s &lt;&lt; std::endl;};
    std::function&lt;void(const std::string&amp; s)&gt; f3 = S();

    f1(&quot;sss&quot;);
    f2(&quot;sss&quot;);
    f3(&quot;sss&quot;);

    return 0;

}</code></pre>
<p>템플릿 인자로 함수의 타입(반환, 인자)를 받아 함수 객체를 보관하는 역할</p>
<h2 id="멤버함수">멤버함수</h2>
<p>멤버함수의 경우엔?</p>
<pre><code class="language-cpp">class A {
    int c;
public:
    A(int c) : c(c) {}
    int some_func() { std::cout &lt;&lt; &quot;내부데이터: &quot; &lt;&lt; c &lt;&lt; std::endl; }
};


int main() {
    A a(5);
    std::function&lt;int()&gt; f1 = a.some_func;
}</code></pre>
<p>내부 함수의 c가 어떤 객체의 c를 참조하는지 알 수 없기에 오류가 발생한다.
즉, 멤버 함수는 객체 없이 단독 호출이 불가능하기 때문에, std::function에 넣을 때는 객체 참조를 인자로 받는 형식으로 감싸야 합니다.</p>
<p>그렇기에 </p>
<pre><code class="language-cpp">class A {
    int c;
public:
    A(int c) : c(c) {}
    int some_func() { std::cout &lt;&lt; &quot;내부데이터: &quot; &lt;&lt; c &lt;&lt; std::endl; }

    int some_const_function() const { std::cout &lt;&lt; &quot;내부데이터, const: &quot; &lt;&lt; c &lt;&lt; std::endl; }
};


int main() {
    A a(5);
        // 비-const 멤버 함수 포인터
    std::function&lt;int(A&amp;)&gt; f1 = &amp;A::some_func;
    f1(a);

    // const 멤버 함수 포인터
    std::function&lt;int(const A&amp;)&gt; f2 = &amp;A::some_const_func;
    f2(a);

}</code></pre>
<p>함수 이름만으로는 멤버 함수 포인터로 암시적 변환되지 않기 때문에 <strong>&amp;A::some_func처럼 명시적 주소 연산자</strong>가 필요합니다.</p>
<h2 id="stdmem_fn">std::mem_fn</h2>
<p>멤버 함수 포인터를 함수 객체로 변환해주는 도우미 함수입니다. 객체를 인자로 넘기기만 하면 됩니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;functional&gt;
#include &lt;vector&gt;

class A {
public:
    void print() const { std::cout &lt;&lt; &quot;mem_fn 예제&quot; &lt;&lt; std::endl; }
};

int main() {
    A a;
    auto f = std::mem_fn(&amp;A::print);
    f(a); // mem_fn을 이용한 멤버 함수 호출

    std::vector&lt;A&gt; vec(3);
    for (const auto&amp; item : vec) f(item); // STL 컨테이너에도 적용 가능
}
</code></pre>
<h2 id="stdbind">std::Bind</h2>
<p>std::bind는 <strong>기존 함수나 멤버 함수에 인자를 고정(bind)</strong> 시켜서 새로운 호출 가능한 객체를 만듭니다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;functional&gt;

void greet(const std::string&amp; name, int times) {
    for (int i = 0; i &lt; times; ++i)
        std::cout &lt;&lt; &quot;Hello, &quot; &lt;&lt; name &lt;&lt; &quot;!\n&quot;;
}

int main() {
    auto say_hello = std::bind(greet, &quot;Alice&quot;, 3);
    say_hello(); // Hello, Alice! x3

    auto say_n_times = std::bind(greet, std::placeholders::_1, 2);
    say_n_times(&quot;Bob&quot;); // Hello, Bob! x2
}
</code></pre>
<p><strong>std::placeholders::_1</strong> 여러 숫자가 있는데 인자의 순서를 나타낸다.</p>
<pre><code class="language-cpp">auto g = std::bind(show, std::placeholders::_2, std::placeholders::_1, 300);</code></pre>
<p>이런식으로 할 경우 g(2,3)이라고 할 경우 -&gt; (3,2)의 순서로 나타난다.</p>
<p>  레퍼런스의 경우 명시적으로 std::ref(s1) 전달해주지 않으면 데이터가 변경되지 않을 수 있는데 인자가 복사되어 전달되기 때문이다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;functional&gt;

void modify(std::string&amp; s) {
    s += &quot; world&quot;;
}

int main() {
    std::string s = &quot;hello&quot;;

    // 값 복사 - 원본 변경 안 됨
    auto f1 = std::bind(modify, s);
    f1();
    std::cout &lt;&lt; s &lt;&lt; std::endl; // 출력: hello

    // 참조로 전달
    auto f2 = std::bind(modify, std::ref(s));
    f2();
    std::cout &lt;&lt; s &lt;&lt; std::endl; // 출력: hello world
}
</code></pre>
<p>std::bind는 인자를 기본적으로 값으로 복사하므로, 원본 객체를 수정하고 싶다면 반드시 <strong>std::ref()로 참조 전달</strong>해야 합니다.</p>
<p>멤버 함수 바인딩의 경우에도</p>
<pre><code class="language-cpp">
class A {
public:
    void hello(const std::string&amp; msg) {
        std::cout &lt;&lt; &quot;A says: &quot; &lt;&lt; msg &lt;&lt; &quot;\n&quot;;
    }
};

A a;
auto f = std::bind(&amp;A::hello, &amp;a, &quot;Hi&quot;);
f(); // A says: Hi
</code></pre>
<pre><code class="language-cpp">void A::hello(A* this, const std::string&amp; msg);</code></pre>
<p>내부적으론 이런형태이기 때문에 꼭 주소를 명시를 해주어야 한다.</p>
<p>  <a href="https://learn.microsoft.com/ko-kr/cpp/standard-library/functional-functions?view=msvc-170">MS functional 헤더에 대한 자세한 참고자료</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[스마트 포인터(unique, shared, weak)]]></title>
            <link>https://velog.io/@creat_my_world/%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0unique-shared-weak</link>
            <guid>https://velog.io/@creat_my_world/%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0unique-shared-weak</guid>
            <pubDate>Fri, 21 Mar 2025 12:58:16 GMT</pubDate>
            <description><![CDATA[<h3 id="✅왜-스마트포인터를-사용하게-됐을까">✅<strong>왜 스마트포인터를 사용하게 됐을까?</strong></h3>
<p>예외처리 과정에서 delete 해야 했던 것을 못하고 넘어가게 되는 경우가 발생
→ 메모리 누수(memory leak)</p>
<p>하지만 C++에서는,
예외가 발생해 함수에서 빠져나가더라도,
그 함수의 <strong>스택</strong>에 정의된 모든 객체는 자동으로 소멸자 호출됨
→ 이 과정을 <strong>stack unwinding(스택 풀기)</strong>이라고 한다</p>
<p>이 아이디어를 차용하여,
포인터를 객체로 감싸고, 그 객체의 소멸자에서 delete를 자동으로 호출하도록 설계했다.</p>
<h4 id="raii-resource-acquisition-is-initialization">RAII (Resource Acquisition Is Initialization)</h4>
<p>자원(포인터, 파일, 락 등)을 생성자에서 획득
해당 객체가 scope을 벗어나면 소멸자에서 자원 자동 해제</p>
<p>즉, 자원을 객체의 <strong>생명주기(lifetime)</strong>에 묶어버린 것.</p>
<h2 id="✅unique-ptr">✅Unique Ptr</h2>
<p>특정객체에 <strong>유일한 소유권</strong>을 부여하는 것이다.
여러 객체가 참조하지 못한다.</p>
<blockquote>
<p><strong>소유권이란?</strong>
이 객체가 더 이상 필요 없을 때, &quot;누가 메모리에서 해제(delete)할 책임이 있는가?&quot; 에 대한 권한을 의미한다.
unique ptr에선 그 소유권을 특정 객체에게 명확히 한다.</p>
</blockquote>
<p><strong>소유권이 중요한 이유</strong></p>
<ol>
<li><p>Dangling Pointer (허공을 가리키는 포인터): A가 객체를 지웠는데, B가 그 객체에 접근하려 할 때</p>
</li>
<li><p>Double Free (중복 해제): A도 지우고, B도 똑같은 객체를 지우려 할 때 발생</p>
</li>
<li><p>Memory Leak (메모리 누수)</p>
</li>
</ol>
<p>따라서 </p>
<pre><code class="language-cpp">std::unique_ptr&lt;Class&gt; ptr(new int(35));</code></pre>
<p>일 경우 </p>
<pre><code class="language-cpp">std::unique_ptr&lt;Class&gt; ptr2 = ptr; //불가능</code></pre>
<p>이 불가능한데 기본적으로 복사 연산자가 없기 때문이다.
그렇기에 소유권을 넘겨주기 위해서는 </p>
<pre><code class="language-cpp">std::unique_ptr&lt;Class&gt; ptr2 = std::move(ptr); </code></pre>
<p>의 rvalue 형태로 <strong>이동</strong>시켜줘야 한다.</p>
<p>함수 인자로 전달할 때는 포인터의 형태로 전달해야한다. unique_ptr형태로 전달할 경우 더 이상 유일한 소유권이 아니게 되기 때문이다.</p>
<pre><code class="language-cpp">void func(int *a);

int main()
{
    std::unique_ptr&lt;int&gt; ptr(new int(35));
    func(ptr.get()) //실제 주소값 전달
}</code></pre>
<h3 id="make_unique">make_unique</h3>
<p>템플릿인자로 전달된 클래스의 생성자 인자들에 직접 완벽한 전달을 한다.</p>
<pre><code class="language-cpp"> auto ptr = std::make_unique&lt;Class&gt;(1);
</code></pre>
<h3 id="컨테이너에-사용">컨테이너에 사용</h3>
<p>vector에 사용할 경우 그래도 유니크 포인터로 전달할 경우 컴파일 에러가 발생한다. -&gt; 벡터에 push_back할 때 복사하여 전달하는데 복사 연산자가 없기 때문이다. 
역시 rvalue 형태로 만들어 넣어줘야 한다.</p>
<pre><code class="language-cpp">vec.push_back(std::move(ptr));
//or
vec.emplace_back(new Class(1));</code></pre>
<h2 id="✅shared_ptr">✅shared_ptr</h2>
<p>유니크 포인터와는 다르게 여러 공유 포인터가 하나의 객체를 가리킬 수 있다.</p>
<p>그럼 delete는 어떻게 할까?
 -&gt;그 객체를 참고 하고 있는 개수<strong>(참조 카운트)가 0</strong>이 될 경우 소멸</p>
<p> 개수를 저장할때 각각의 포인터 객체에 넣으면 업데이트를 어떻게 시키지? 
         -&gt; 처음에 제어 블록(control block)을 동적으로 할당하고 각 포인터 객체들이 이를 공유하며 필요한 정보를 얻거나 업데이트 시킴.</p>
<pre><code class="language-cpp">std::shared_ptr&lt;int&gt; ptr(new int(10));//
std::shared_ptr&lt;int&gt; ptr2 = ptr;</code></pre>
<p>복사 연산자가 있기 때문에 = 사용 가능하며 위 경우엔 한 객체를 2개가 가리키게 된다.</p>
<p>[ 제어 블록 (ref count 포함) ] ← 여러 shared_ptr이 이걸 공유
         ↑
    [shared_ptr 객체]
         ↓
    [실제 데이터 (int)]</p>
<p><strong>제어 블록(control block)</strong>에는 다음이 포함됨:
참조 카운트 (use_count)
삭제자 (deleter)
weak reference count (shared_ptr + weak_ptr 관리용)</p>
<h3 id="make_shared">make_shared</h3>
<p>일반적으로 공유 포인터를 만들경우 객체 그리고 제어블록에 대한 동적할당 2번이 일어난다. make_shared을 쓸 경우 두 크기를 합친만큼의 동적할당 1번이 일어나므로 속도가 더 빠르다.</p>
<pre><code class="language-cpp">std::shared_ptr&lt;A&gt; p1 = std::make_shared&lt;A&gt;();
### ```
#### 주의할 점
인자로 주소값을 전달할 경우 첫 번째 객체로 갖는다고 인식하여 여러 개가 같은 주소값을 가리켜도 각각 1개씩 가리킨다고 생각한다.
-&gt; 즉, 서로 다른 제어블록을 계속 생성함.
```cpp
 A* a = new A();
 std::shared_ptr&lt;A&gt; ptr1(a);
 std::shared_ptr&lt;A&gt; ptr2(a);</code></pre>
<p>-&gt; 참조카운트에 대한 오해로 이미 소멸시킨 객체를 또 소멸시켜 오류가 발생한다.</p>
<h3 id="enable_shared_from_this">enable_shared_from_this</h3>
<p>shared_ptr<T>는 T*만 가지고 있다고 해서 내부적으로 그걸 관리할 수 없음</p>
<p>객체 안에서 자신을 shared_ptr로 다시 만들려면 자신이 어느 제어 블록에 소속돼 있는지 알아야 함</p>
<pre><code class="language-cpp">class ex
{
     std::shared_ptr&lt;ex&gt; get_shared_ptr() { return std::shared_ptr&lt;ex&gt;(this); }
}

int main() {
 std::shared_ptr&lt;ex&gt; pa1 = std::make_shared&lt;ex&gt;();
 std::shared_ptr&lt;ex&gt; pa2 = pa1-&gt;get_shared_ptr(); //오류 발생

 }</code></pre>
<p>-&gt;이럴 경우 오류가 발생하는데 이때
enable_shared_from_this 클래스를 상속받아서 사용하면 된다.</p>
<pre><code class="language-cpp">class ex: public std::enable_shared_from_this&lt;ex&gt; 
{
     std::shared_ptr&lt;ex&gt; get_shared_ptr() { return shared_from_this();  }
}

int main() {
 std::shared_ptr&lt;ex&gt; pa1 = std::make_shared&lt;ex&gt;();
 std::shared_ptr&lt;ex&gt; pa2 = pa1-&gt;get_shared_ptr(); 

 }</code></pre>
<p>enable_shared_from_this를 상속하면,
std::weak_ptr<ex>를 내부에 저장해두고,
shared_from_this() 호출 시 그걸 shared_ptr로 승격해서 반환함.</p>
<h2 id="✅weak_ptr">✅weak_ptr</h2>
<h3 id="stdweak_ptr">std::weak_ptr</h3>
<p>weak_ptr는 shared_ptr와 함께 사용하는 보조 스마트 포인터로,</p>
<blockquote>
<p><strong>객체를 소유하지 않고 참조만 하기 위한 목적으로 사용된다.</strong>
순환 참조 문제를 회피할 수 있는 유일한 안전한 방법
주로 부모-자식 관계, 콜백, 이벤트 리스너, 캐시 등에서 사용</p>
</blockquote>
<h3 id="🔄-순환-참조-문제-circular-reference">🔄 순환 참조 문제 (Circular Reference)</h3>
<p>shared_ptr는 참조 카운트를 기반으로 객체를 소멸시킴.
그런데 아래와 같은 경우가 생기면 문제가 생김:
A → (shared_ptr) → B<br>B → (shared_ptr) → A
서로가 서로를 shared_ptr로 참조하면 참조 카운트가 0이 될 수 없어,
객체가 소멸되지 않는 메모리 누수가 발생한다.
➡ 이걸 해결하려고 나온 게 바로 weak_ptr이다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;memory&gt;

struct B;
struct A {
    std::shared_ptr&lt;B&gt; b_ptr;
};

struct B {
    std::weak_ptr&lt;A&gt; a_ptr; //  
  };

int main() {
    std::shared_ptr&lt;A&gt; a = std::make_shared&lt;A&gt;();
    std::shared_ptr&lt;B&gt; b = std::make_shared&lt;B&gt;();

    a-&gt;b_ptr = b;
    b-&gt;a_ptr = a; // shared_ptr이었다면 순환 참조 발생

    // 둘 다 scope 벗어나면 자동으로 소멸
}
</code></pre>
<h3 id="lock-함수">lock() 함수</h3>
<p>weak_ptr은 객체가 이미 소멸되었을 수도 있기 때문에,
바로 접근하지 못하고 lock() 함수를 통해 접근해야 한다.</p>
<pre><code class="language-cpp">std::weak_ptr&lt;A&gt; weak;
if (auto shared = weak.lock()) {
    // shared는 유효한 shared_ptr → 객체가 살아 있음
    shared-&gt;doSomething();
} else {
    // 객체는 이미 소멸됨
}</code></pre>
<h3 id="제어-블록-control-block">제어 블록 (Control Block)</h3>
<p>shared_ptr, weak_ptr 모두 내부적으로 제어 블록을 공유함
제어 블록에는 다음 정보가 있음:</p>
<p>use count == 현재 shared_ptr 참조 수
weak count == 현재 weak_ptr 참조 수
deleter    삭제자 정보
객체는 use count == 0일 때 소멸됨
<strong>제어 블록 자체는 shared_ptr + weak_ptr 모두 0일 때 메모리에서 해제</strong>됨</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[rvalue, lvalue 관련 ]]></title>
            <link>https://velog.io/@creat_my_world/rvalue-lvalue-%EA%B4%80%EB%A0%A8</link>
            <guid>https://velog.io/@creat_my_world/rvalue-lvalue-%EA%B4%80%EB%A0%A8</guid>
            <pubDate>Fri, 21 Mar 2025 11:34:01 GMT</pubDate>
            <description><![CDATA[<p>rvalue는 일반적으로 <strong>&quot;임시 값&quot;, &quot;이름 없는 값&quot;</strong>으로
즉, 한 번 쓰고 버리는 값이라고 생각하면 된다.</p>
<p>그렇기에 lvalue 레퍼런스를 하듯 rvalue 레퍼런스를 사용할 경우 오류가 발생할 수 있다. 따라서 &amp;&amp;라는 기호를 사용하여 rvalue 레퍼런스를 사용할 수 있다.
**
왜 사용하는 것일까?**</p>
<blockquote>
<p>불필요한 복사를 피해서 성능(메모리, 속도...)을 크게 향상시키기 위해서이다.</p>
</blockquote>
<p>복사를 할 경우 깊은 복사는 비용이 매우 크다. (메모리 할당, 데이터 복제 등)
<strong>이동</strong>은 그냥 포인터만 옮기면 되기 때문이다. (자원 뺏기)</p>
<h2 id="관련-함수-및-추가-정보">관련 함수 및 추가 정보..</h2>
<h3 id="stdmove함수">std::move함수</h3>
<p>lvalue를 rvalue로 형 변환 해주는 역할 -&gt; 이동 연산자 (&amp;&amp; a)를 사용하는데 쓰임.
우측값 레퍼런스를 할 때 쓰인다. </p>
<pre><code class="language-cpp">#include &lt;utility&gt;  // for std::move
#include &lt;iostream&gt;

template&lt;typename T&gt;
class tt
{
    int a;

public:
    // 기본 생성자
    tt() : a(0) {}

    // 복사 생성자
    tt(const tt&amp; t)
    {
        a = t.a;
        std::cout &lt;&lt; &quot;복사 생성자&quot; &lt;&lt; std::endl;
    }

    // 이동 생성자
    tt(tt&amp;&amp; t)
    {
        a = t.a; // int는 move 의미 없지만 일단 값 복사
        std::cout &lt;&lt; &quot;이동 생성자&quot; &lt;&lt; std::endl;
    }
};

int main()
{
    tt&lt;int&gt; t;                      // 기본 생성자
    tt&lt;int&gt; t2(std::move(t));       // 이동 생성자
}</code></pre>
<h3 id="stdforward">std::forward&lt;&gt;</h3>
<p>✅ std::forward란?
&quot;넘겨받은 값을 원래의 속성(lvalue인지 rvalue인지) 그대로 다음 함수에 전달하는 것&quot;</p>
<p>이걸 <strong>perfect forwarding (완벽 전달)</strong>이라고 한다.</p>
<p>🔍 왜 필요한가?
📦 일반적인 템플릿 함수에서</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void wrapper(T arg) {
    callee(arg); // 문제 발생 가능
}</code></pre>
<p>여기서 arg는 항상 <strong>좌측값(lvalue)</strong>로 취급됨
원래 rvalue였던 값도 lvalue로 바뀜!
즉, 원래 이렇게 부르면:</p>
<p>wrapper(std::string(&quot;hi&quot;)); // &lt;- 이건 rvalue!
그런데 callee(arg)로 넘기면 arg는 이름 있는 변수 → lvalue
→ 그래서 callee는 복사 생성자를 호출하게 됨 ❌</p>
<p>✅ 해결법: std::forward</p>
<pre><code class="language-cpp">template &lt;typename T&gt;
void wrapper(T&amp;&amp; arg) {
    callee(std::forward&lt;T&gt;(arg)); // 💥 여기서 원래 성질 유지됨!
}</code></pre>
<p>여기서 T&amp;&amp;는 <strong>universal reference</strong> (또는 forwarding reference)
std::forward<T>(arg)는
T가 lvalue면 → lvalue로 전달
T가 rvalue면 → rvalue로 전달
즉, 전달받은 값의 <strong>원래 성격(lvalue/rvalue)</strong>을 그대로 전달하는 게 핵심!</p>
<p>  <a href="https://learn.microsoft.com/ko-kr/cpp/cpp/rvalue-reference-declarator-amp-amp?view=msvc-170">rvalue 레퍼런스 관련 MS 문서</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[예외 처리]]></title>
            <link>https://velog.io/@creat_my_world/%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@creat_my_world/%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Thu, 20 Mar 2025 13:10:01 GMT</pubDate>
            <description><![CDATA[<h3 id="✅-1-예외exception란">✅ 1. 예외(Exception)란?</h3>
<p>C++에서 <strong>예외(Exception)</strong>는 프로그램 실행 중 오류 또는 비정상적인 상황이 발생했을 때 이를 감지하고 적절히 처리하는 메커니즘이다.
예외 처리를 사용하면 프로그램이 비정상적으로 종료되는 것을 방지하고, 오류를 보다 체계적으로 관리할 수 있다.</p>
<h3 id="✅-2-throw-try-catch-기본-개념">✅ 2. throw, try, catch 기본 개념</h3>
<p>C++ 예외 처리는 세 가지 주요 키워드로 구성된다.</p>
<p><strong>throw</strong>    예외를 발생시킬 때 사용
<strong>try</strong>    예외 발생 가능성이 있는 코드를 감싸는 블록
<strong>catch</strong>    예외가 발생했을 때 실행할 코드 블록</p>
<h3 id="✅-3-예외-처리-기본-구조">✅ 3. 예외 처리 기본 구조</h3>
<pre><code class="language-cpp">try {
    // 예외가 발생할 가능성이 있는 코드
    throw 42;  // 예외 발생
} 
catch (int e) {  
    // 예외 처리 코드
    std::cout &lt;&lt; &quot;Caught exception: &quot; &lt;&lt; e &lt;&lt; std::endl;
}</code></pre>
<p>✔ throw → 예외 발생 시, 예외 객체를 전달함
✔ try → 예외 발생 가능성이 있는 코드 블록을 감싸줌
✔ catch → 발생한 예외를 받아 처리</p>
<h3 id="✅-4-예제-코드-예외-발생-및-처리">✅ 4. 예제 코드: 예외 발생 및 처리</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void testFunction() {
    std::cout &lt;&lt; &quot;Throwing an exception...&quot; &lt;&lt; std::endl;
    throw std::runtime_error(&quot;Something went wrong!&quot;);  // 예외 발생
}

int main() {
    try {
        testFunction();
    } 
    catch (const std::runtime_error&amp; e) {
        std::cout &lt;&lt; &quot;Caught exception: &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
    }

    std::cout &lt;&lt; &quot;Program continues...&quot; &lt;&lt; std::endl;
    return 0;
}</code></pre>
<p>🔹 실행 결과:
Throwing an exception...(예외 발생)
Caught exception: Something went wrong!(해당 예외 타입에 대한 정보를 받아 실행)
Program continues...
✔ 예외가 발생하면 즉시 catch 블록으로 이동하여 처리됨
✔ 예외 처리 후에도 프로그램이 정상적으로 실행됨</p>
<h3 id="✅-5-여러-개의-catch-블록-사용">✅ 5. 여러 개의 catch 블록 사용</h3>
<p>예외 타입에 따라 다른 방식으로 처리 가능</p>
<pre><code class="language-cpp">try {
    throw 42;  // 예외 발생
} 
catch (int e) {
    std::cout &lt;&lt; &quot;Caught an integer: &quot; &lt;&lt; e &lt;&lt; std::endl;
} 
catch (const std::runtime_error&amp; e) {
    std::cout &lt;&lt; &quot;Caught a runtime_error: &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
} 
catch (...) {  // 모든 예외를 처리하는 블록 switch의 default처럼
    std::cout &lt;&lt; &quot;Caught an unknown exception&quot; &lt;&lt; std::endl;
}</code></pre>
<p>✔ <strong>특정 예외 타입</strong>에 맞는 catch 블록이 실행됨 
✔ <strong>catch (...)는 모든 예외를 처리</strong>하는 블록 (예외 종류를 모를 때 사용)</p>
<h3 id="✅-6-예외-전파exception-propagation"><strong>✅ 6. 예외 전파(Exception Propagation)</strong></h3>
<p>예외는 함수를 넘어서 상위 함수로 전파될 수 있다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void functionA() {
    throw std::runtime_error(&quot;Error in functionA&quot;);
}

void functionB() {
    functionA();  // functionA에서 예외 발생
}

int main() {
    try {
        functionB();  // functionB가 호출됨
    } 
    catch (const std::runtime_error&amp; e) {
        std::cout &lt;&lt; &quot;Caught exception: &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
    }
}</code></pre>
<p>🔹 실행 결과:
Caught exception: Error in functionA
✔ 예외가 발생하면 functionA → functionB → main으로 전파됨
✔ main에서 예외가 처리되지 않으면 프로그램이 비정상 종료됨
**
만약 부모,자식 클래스에 대해서 받을 경우 catch순서에 부모 클래스를 먼저 둘 경우 
Parent&amp; p = Child()✅
 Child&amp; p = Parent()❌
도 가능하므로 자식클래스 예외타입에 대해서도 부모 클래스에 대한 catch가 실행되므로 자식 클래스를 앞에 두는 것이 좋다.**</p>
<h3 id="✅-7-스택-풀기-stack-unwinding">✅ 7. 스택 풀기 (Stack Unwinding)</h3>
<p><strong>스택 풀기(Stack Unwinding)</strong>란 예외가 발생하여 함수가 종료될 때, 지역 변수들이 자동으로 소멸되는 과정을 의미한다.</p>
<p>📌 스택 풀기의 원리:
예외 발생 시, 현재 함수가 즉시 종료됨.
지역 변수들의 소멸자가 자동으로 호출됨.
예외가 상위 함수로 전파되면서, 그 과정에서 할당된 모든 지역 변수들이 해제됨.
catch 블록에서 예외를 처리하면 예외 전파가 멈춤.
📌 예제 (소멸자 호출 확인)</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

struct Test {
    std::string name;
    Test(std::string n) : name(n) { std::cout &lt;&lt; name &lt;&lt; &quot; created\n&quot;; }
    ~Test() { std::cout &lt;&lt; name &lt;&lt; &quot; destroyed\n&quot;; }
};

void functionA() {
    Test a(&quot;A&quot;);
    throw std::runtime_error(&quot;Exception in functionA&quot;);
}

void functionB() {
    Test b(&quot;B&quot;);
    functionA();
}

int main() {
    try {
        functionB();
    } 
    catch (const std::runtime_error&amp; e) {
        std::cout &lt;&lt; &quot;Caught exception: &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
    }
}</code></pre>
<p>🔹 실행 결과:
B created
A created
A destroyed
B destroyed
Caught exception: Exception in functionA
✔ 예외가 발생하면 지역 변수들이 역순으로 소멸됨.
✔ 스택 풀기가 동작하여 자원 누수가 방지됨.
<strong>유의할 점 -&gt; 생성자에서 예외 발생 시 소멸자가 호출되지 않음!@</strong></p>
<h3 id="✅-8-사용자-정의-예외-클래스">✅ 8. 사용자 정의 예외 클래스</h3>
<p>C++에서는 std::exception을 상속받아 사용자 정의 예외 클래스를 만들 수 있다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;stdexcept&gt;

class MyException : public std::exception {
public:
    const char* what() const noexcept override {
        return &quot;Custom exception occurred!&quot;;
    }
};

void test() {
    throw MyException();
}

int main() {
    try {
        test();
    } 
    catch (const MyException&amp; e) {
        std::cout &lt;&lt; &quot;Caught: &quot; &lt;&lt; e.what() &lt;&lt; std::endl;
    }
}</code></pre>
<p>✔ std::exception을 상속받아 what()을 오버라이드하여 예외 메시지를 정의 가능.
✔ 사용자 정의 예외를 통해 더 세밀한 예외 처리가 가능.</p>
<p><strong>what()</strong>-&gt;예외 발생 시 해당 예외에 대한 설명 메시지(문자열)를 반환하는 역할</p>
<h3 id="✅-9-noexcept와-예외-처리">✅ 9. noexcept와 예외 처리</h3>
<p>noexcept 키워드를 사용하면 함수에서 예외가 발생하지 않음을 명시할 수 있다.</p>
<pre><code class="language-cpp">void safeFunction() noexcept {
    std::cout &lt;&lt; &quot;This function never throws exceptions.\n&quot;;
}</code></pre>
<p>✔ noexcept가 붙은 함수에서 예외가 발생하면 프로그램이 강제 종료됨.</p>
<p>✔ noexcept가 붙었다고 해서 절대 예외를 발생시키지 않는 것은 아니다.
-&gt; 컴파일러가 예외를 발생시키지 않는다고 생각하고 컴파일 하는 것임.</p>
<p>-✔ 예외를 절대 발생시키지 않는 함수에 사용하면 최적화에 도움이 됨.
✔ C++11 부터 소멸자는 기본적으로 noexcept임.</p>
<h3 id="추가자료-stdexcepth">추가자료 &lt;stdexcept.h&gt;</h3>
<p><a href="https://learn.microsoft.com/ko-kr/cpp/standard-library/stdexcept?view=msvc-170">MS &lt;stdexcept.h&gt;에 대한 자세한 자료들 클래스들 등등...</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[STL]algorithm]]></title>
            <link>https://velog.io/@creat_my_world/STLalgorithm</link>
            <guid>https://velog.io/@creat_my_world/STLalgorithm</guid>
            <pubDate>Thu, 20 Mar 2025 12:05:56 GMT</pubDate>
            <description><![CDATA[<h3 id="1️⃣-람다-함수-lambda-function">1️⃣ 람다 함수 (Lambda Function)</h3>
<p>람다 함수는 <strong>익명 함수(anonymous function)</strong>로, 간결하게 정의하여 코드의 가독성을 높이고, 함수 객체(functor) 없이도 간편하게 사용할 수 있다.</p>
<p>✅ 람다 함수 기본 문법</p>
<pre><code class="language-cpp">Copy code
[capture](parameter_list) -&gt; return_type { function_body };</code></pre>
<p>✅ 예제</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

int main() {
    auto add = [](int a, int b) { return a + b; };
    std::cout &lt;&lt; add(3, 5) &lt;&lt; std::endl;  // 8
}</code></pre>
<p>[] : 캡처 리스트 (외부 변수를 사용할 수 있게 함)
-&gt; 클래스처럼 객체에 대한 변수를 사용할 경우엔 [this]를 통해 인스턴스에 대한 변수임을 알려야 함.
[&amp;] -&gt; 모든 외부 변수를 레퍼런스로
[=] -&gt; 모든 외부 변수를 복사본으로
[&amp;a ,b] -&gt; 반반 </p>
<p>(int a, int b) : 매개변수 리스트
{ return a + b; } : 함수 본문</p>
<h3 id="2️⃣-stdremove">2️⃣ std::remove</h3>
<p>std::remove는 특정 값을 배열 또는 컨테이너에서 삭제하는 것처럼 보이게 한다. 실제로 원소를 삭제하는 것이 아니라, 제거된 요소들을 컨테이너의 끝으로 이동시키고, 새로운 끝을 가리키는 반복자를 반환한다.</p>
<p>✅ 사용법</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {1, 2, 3, 4, 5, 3, 6};

    vec.erase(std::remove(vec.begin(), vec.end(), 3), vec.end());

    for (int v : vec) std::cout &lt;&lt; v &lt;&lt; &quot; &quot;;  // 1 2 4 5 6
}</code></pre>
<p>💡 주의: std::remove는 원소를 삭제하는 것이 아니라, 새로운 위치로 이동시키므로 erase와 함께 사용해야 한다.</p>
<h3 id="3️⃣-stdremove_if">3️⃣ std::remove_if</h3>
<p>std::remove_if는 조건을 만족하는 요소들을 제거하는 버전이다.</p>
<p>✅ 사용법</p>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {1, 2, 3, 4, 5, 6};

    vec.erase(std::remove_if(vec.begin(), vec.end(), [](int x) { return x % 2 == 0; }), vec.end());

    for (int v : vec) std::cout &lt;&lt; v &lt;&lt; &quot; &quot;;  // 1 3 5
}</code></pre>
<p>💡 람다 함수 사용 가능하여 더욱 유연하게 조건을 설정할 수 있다.</p>
<h3 id="4️⃣-stdsort">4️⃣ std::sort</h3>
<p>std::sort는 <strong>빠른 정렬(QuickSort, HeapSort, IntroSort를 혼합한 최적화된 정렬 알고리즘)</strong>을 사용하여 컨테이너를 정렬한다.</p>
<p>✅ 기본 사용법 (오름차순 정렬)</p>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {5, 2, 9, 1, 5, 6};

    std::sort(vec.begin(), vec.end());

    for (int v : vec) std::cout &lt;&lt; v &lt;&lt; &quot; &quot;;  // 1 2 5 5 6 9
}</code></pre>
<p>✅ 내림차순 정렬 (람다 사용)</p>
<pre><code class="language-cpp">
std::sort(vec.begin(), vec.end(), [](int a, int b) { return a &gt; b; });</code></pre>
<h3 id="5️⃣-stdstable_sort">5️⃣ std::stable_sort</h3>
<p>std::stable_sort는 안정적인 정렬 알고리즘으로, 같은 값에 대한 상대적인 순서를 유지한다.</p>
<p>✅ 예제</p>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

struct Person {
    std::string name;
    int age;
};

int main() {
    std::vector&lt;Person&gt; people = {{&quot;Alice&quot;, 30}, {&quot;Bob&quot;, 25}, {&quot;Charlie&quot;, 30}};

    std::stable_sort(people.begin(), people.end(), [](const Person &amp;a, const Person &amp;b) {
        return a.age &lt; b.age;
    });

    for (const auto &amp;p : people) std::cout &lt;&lt; p.name &lt;&lt; &quot; &quot;;  // Bob Alice Charlie
}</code></pre>
<p>💡 정렬 후에도 Alice와 Charlie의 순서가 유지됨 (stable)</p>
<h3 id="6️⃣-stdpartial_sort">6️⃣ std::partial_sort</h3>
<p>std::partial_sort는 컨테이너의 일부만 정렬하는 알고리즘으로, 상위 N개의 요소만 정렬할 때 유용하다.</p>
<p>✅ 예제</p>
<pre><code class="language-cpp">

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {10, 5, 8, 1, 7, 9};

    std::partial_sort(vec.begin(), vec.begin() + 3, vec.end());

    for (int v : vec) std::cout &lt;&lt; v &lt;&lt; &quot; &quot;;  // 1 5 7 10 8 9
}</code></pre>
<p>💡 처음 3개의 요소만 정렬됨, 나머지는 정렬되지 않음.</p>
<h3 id="7️⃣-stdtransform">7️⃣ std::transform</h3>
<p>std::transform은 컨테이너의 각 요소를 변환하여 새로운 컨테이너에 저장할 때 사용한다.</p>
<p>✅ 예제 (각 요소를 2배로 변환)</p>
<pre><code class="language-cpp">
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {1, 2, 3, 4, 5};
    std::vector&lt;int&gt; result(vec.size());

    std::transform(vec.begin(), vec.end(), result.begin(), [](int x) { return x * 2; });

    for (int v : result) std::cout &lt;&lt; v &lt;&lt; &quot; &quot;;  // 2 4 6 8 10
}</code></pre>
<p>💡 원본 컨테이너는 유지하면서 변환된 결과를 다른 컨테이너에 저장할 수 있음.</p>
<h3 id="8️⃣-stdfind">8️⃣ std::find</h3>
<p>std::find는 컨테이너에서 특정 값을 찾아 첫 번째 위치를 반환하는 알고리즘이다.</p>
<p>✅ 사용법</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {10, 20, 30, 40, 50};

    auto it = std::find(vec.begin(), vec.end(), 30);

    if (it != vec.end()) {
        std::cout &lt;&lt; &quot;Found: &quot; &lt;&lt; *it &lt;&lt; std::endl;  // Found: 30
    } else {
        std::cout &lt;&lt; &quot;Not Found&quot; &lt;&lt; std::endl;
    }
}</code></pre>
<p>첫 번째 30을 찾으면 반복자를 반환하며, 없으면 vec.end()를 반환한다.</p>
<h3 id="9️⃣-stdfind_if">9️⃣ std::find_if</h3>
<p>std::find_if는 특정 조건을 만족하는 첫 번째 요소를 찾는 알고리즘이다.</p>
<p>✅ 사용법</p>
<pre><code>
#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {10, 21, 33, 40, 50};

    auto it = std::find_if(vec.begin(), vec.end(), [](int x) { return x % 2 == 1; });

    if (it != vec.end()) {
        std::cout &lt;&lt; &quot;First odd number: &quot; &lt;&lt; *it &lt;&lt; std::endl;  // First odd number: 21
    } else {
        std::cout &lt;&lt; &quot;No odd numbers found&quot; &lt;&lt; std::endl;
    }
}</code></pre><p>람다 함수를 사용해 홀수(x % 2 == 1)인 첫 번째 요소를 찾는다.</p>
<p>set,map과 같은 컨테이너는 이미 내부 구조를 알고 있어 해당 컨테이너의 find함수를 쓰는 것이 더 빠르(효율적)이다. 내부적으로 find함수가 없을 경우 사용하면 유용하다.</p>
<h3 id="🔟-stdall_of"><strong>🔟 std::all_of</strong></h3>
<p>std::all_of는 컨테이너의 <strong>모든 요소</strong>가 특정 조건을 만족하는지 확인하는 알고리즘이다.</p>
<p>✅ 사용법</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {2, 4, 6, 8, 10};

    bool allEven = std::all_of(vec.begin(), vec.end(), [](int x) { return x % 2 == 0; });

    if (allEven) {
        std::cout &lt;&lt; &quot;All elements are even.&quot; &lt;&lt; std::endl;  // 출력됨
    } else {
        std::cout &lt;&lt; &quot;Not all elements are even.&quot; &lt;&lt; std::endl;
    }
}</code></pre>
<p>✔️ 모든 요소가 짝수(x % 2 == 0)인지 검사
✔️ 모든 요소가 조건을 만족하면 true, 그렇지 않으면 false 반환</p>
<h3 id="1️⃣1️⃣-stdany_of">1️⃣1️⃣ std::any_of</h3>
<p>std::any_of는 컨테이너의 <strong>하나 이상의 요소</strong>가 특정 조건을 만족하는지 확인하는 알고리즘이다.</p>
<p>✅ 사용법</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;

int main() {
    std::vector&lt;int&gt; vec = {2, 4, 6, 9, 10};

    bool hasOdd = std::any_of(vec.begin(), vec.end(), [](int x) { return x % 2 == 1; });

    if (hasOdd) {
        std::cout &lt;&lt; &quot;There is at least one odd number.&quot; &lt;&lt; std::endl;  // 출력됨
    } else {
        std::cout &lt;&lt; &quot;No odd numbers found.&quot; &lt;&lt; std::endl;
    }
}</code></pre>
<p>✔️ 하나라도 홀수(x % 2 == 1)인 요소가 있으면 true 반환
✔️ 모든 요소가 조건을 만족하지 않으면 false 반환</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[<백준 알고리즘> KMP 알고리즘 1786번]]></title>
            <link>https://velog.io/@creat_my_world/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-KMP-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-1786%EB%B2%88</link>
            <guid>https://velog.io/@creat_my_world/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-KMP-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-1786%EB%B2%88</guid>
            <pubDate>Fri, 07 Feb 2025 17:35:47 GMT</pubDate>
            <description><![CDATA[<h2 id="kmp-알고리즘">KMP 알고리즘</h2>
<p>문자열을 탐색할 때 사용하는 알고리즘이다.</p>
<p>핵심 아이디어는 찾고자 하는 문자열의 접두사(prefix)와 접미사(suffix)에 대하여 일치하는 개수(길이)를 배열로 저장한 후에 탐색할 때 사용하는 것이다.</p>
<p>예를 들어 찾고자 하는 문자열 p가 ababc 일 경우 
i = 0, 0
i = 1, 0
i = 2, aba 에서 a, a이므로 1
i = 3, abab에서 ab와 ab이므로 2
i = 4, 접미사가 c로 시작하므로 일치하는 것이 없다 0</p>
<p>이렇게 배열을 만든 후 찾고자하는 문자열 s가 ababdababcababc일 경우</p>
<p>j = 0(p에서 쓰이는 인덱스), i = 0 (s에서 쓰이는 인덱스)</p>
<p>처음에 ababd에 대하여 abab까지 같으므로 j = 4 , i = 4이고 다음 상황에 c != d이므로 j를 옮겨 줘야 한다 이때 미리 구해둔 pi배열을 쓰는데 위에서 보든 pi[3]일때 즉 abab였을 때 접두사 == 접미사 중 가장 긴 것의 길이가 2인 ab를 저장해두었으므로 j = 2인데 [2]또한 다르기 때문에 한 번더 옮겨주면 j = 0으로 다시 시작하게 된다.</p>
<p>쉽게 얘기해서 굳이 처음부터 다시 비교하는 것이 아닌 미리 저장해둔 위치로 이동해서 더 효율적으로 탐색하는 것이다.</p>
<p>이후 계속 보면 ababc가 겹치므로 정답을 추가하고  j = 0이 되어 다시 진행 하면 ababc가 또 나오므로 총 2개의 문자열이 탐색된다.</p>
<h3 id="백준-1786번-문제">백준 1786번 문제</h3>
<p><a href="https://www.acmicpc.net/problem/1786">백준 1786번</a></p>
<pre><code>
#include &lt;iostream&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;cstring&gt;
#include &lt;cmath&gt;
#include &lt;stack&gt;
#include &lt;queue&gt;
#include &lt;set&gt;
#include &lt;map&gt;
#include &lt;unordered_map&gt;
#include &lt;unordered_set&gt;
#include &lt;string&gt;
#include &lt;functional&gt;


using namespace std;

int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };




vector&lt;int&gt; getPi(string p)
{
    int m = p.size();
    int j = 0;

    vector&lt;int&gt; pi(m, 0);

    for (int i = 1; i &lt; m; i++)
    {
        while (j &gt; 0 &amp;&amp; p[i] != p[j])
            j = pi[j - 1];

        if (p[i] == p[j])
            pi[i] = ++j;
    }

    return pi;
}

vector&lt;int&gt; kmp(string s, string p)
{
    vector&lt;int&gt;ans;

    auto pi = getPi(p);

    int n = s.size(); int m = p.size(); int j = 0;

    for (int i = 0; i &lt; n; i++)
    {
        while (j &gt; 0 &amp;&amp; s[i] != p[j])
            j = pi[j - 1];

        if (s[i] == p[j])
        {
            if (j == m - 1)
            {
                ans.push_back(i - m + 1);
                j = pi[j];
            }
            else
            {
                j++;
            }
        }
    }

    return ans;
}


int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);


    string T, P;

    getline(cin, T);

    getline(cin, P);


    vector&lt;int&gt; a = kmp(T, P);

    cout &lt;&lt; a.size() &lt;&lt; &quot;\n&quot;;

    for (int i = 0; i &lt; a.size(); i++)
    {
        cout &lt;&lt; a[i] + 1 &lt;&lt; &quot; &quot;;
    }

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[<백준 알고리즘> 2263번 트리의 순회(분할정복)]]></title>
            <link>https://velog.io/@creat_my_world/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2263%EB%B2%88-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%88%9C%ED%9A%8C%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5</link>
            <guid>https://velog.io/@creat_my_world/%EB%B0%B1%EC%A4%80-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-2263%EB%B2%88-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%88%9C%ED%9A%8C%EB%B6%84%ED%95%A0%EC%A0%95%EB%B3%B5</guid>
            <pubDate>Sat, 01 Feb 2025 14:24:34 GMT</pubDate>
            <description><![CDATA[<p><a href="https://www.acmicpc.net/problem/2263">백준 2263번</a></p>
<h2 id="접근-방식">접근 방식</h2>
<p>주어진 inorder과 postorder를 고려해 봤을 때 postorder을 통해서 루트를 얻어올 수 있고 inorder를 통하여 미리 구한 루트를 기준으로 왼쪽 서브트리 오른쪽 서브트리를 구할 수 있다는 것을 알아냈다.</p>
<ol>
<li><p>이를 구현하기 위해 postorder의 가장 마지막 값은 무조건 root의 값이므로 이를 기점으로 시작하여 root의 왼쪽 subroot 오른쪽 subroot를 구하는 방식으로 접근한다.</p>
</li>
<li><p>subroot를 구하기 위하여 처음에 쓴 방식은 루트를 구했다면 이를 기준으로 inorder에서 해당 루트 인덱스 왼쪽은 왼쪽 서브트리 set ,오른쪽은 오른쪽 서브트리 set에 넣어 postorder을 맨뒤에서 부터 순회할 때 오른쪽 왼쪽 각각 가장 먼저 set안에 해당하는 값이면 이것이 그 subtree의 root임을 이용하였다.</p>
</li>
<li><p>자료구조의 트리처럼 왼쪽, 오른쪽 포인터를 이용한 구조체를 만들어서 하였는데 시간초과도 나고 불필요하다는 생각이 들어 재귀를 통해 함수에 접근할때 바로 출력하면 그것이 preorder이기 때문에 구조체 방법을 없애고 바로바로 출력하는 방법으로 바꾸었다.</p>
</li>
<li><p>여전히 시간초과가 나서 subroot를 찾을때 오른쪽 subroot는 postorder배열에서 현재 root의 바로 옆에있는 것이기에 왼쪽 subroot를 구할때 오른쪽 개수만큼 뺀후에 시작하도록 하였는데 이래도 여전히 시간초과가 발생했다.</p>
</li>
<li><p>마지막으로 시도한 방법은 오른쪽은 현재 root바로 옆 인덱스의 값을 반환하도록 하고 왼쪽은 아까 시도한 방식에서 for문을 없애고 현재 인덱스에서 오른쪽 서브트리의 개수 + 1의 값을 뺴서 이 역시도 해당 인덱스에 위치한 배열 값을 출력하도록 했고 문제가 해결이 됐다.</p>
</li>
</ol>
<p>처음 접근 방식은 좋았는데 이를 효율적으로 구현하지 못하여 좀 헤맸던 것 같다.</p>
<h3 id="해당-코드">해당 코드</h3>
<pre><code>
#include &lt;iostream&gt;
#include &lt;queue&gt;
#include &lt;algorithm&gt;
#include &lt;vector&gt;
#include &lt;cstring&gt;
#include &lt;cmath&gt;
#include &lt;stack&gt;
#include &lt;queue&gt;
#include &lt;set&gt;
#include &lt;map&gt;
#include &lt;unordered_map&gt;
#include &lt;unordered_set&gt;
#include &lt;string&gt;


using namespace std;

int dx[4] = { 0,0,-1,1 };
int dy[4] = { -1,1,0,0 };

int N;

int InOrder[100001] = { 0 };
int PostOrder[100001] = { 0 };

unordered_map&lt;int,int&gt;InOrderIdx;
unordered_map&lt;int,int&gt;PostOrderIdx;


int FindLeftTreeRoot(int rootIdx, int postRootIdx, int end) {

    int rightNums = end - rootIdx;  
    int leftRootIdx = postRootIdx - rightNums - 1;  

    if (leftRootIdx &gt;= 1) {
        return PostOrder[leftRootIdx];
    }
    return -999;
}

int FindRightTreeRoot(int rootIdx, int postRootIdx) {

    int rightRootIdx = postRootIdx - 1;  

    if (rightRootIdx &gt;= 1) {
        return PostOrder[rightRootIdx];
    }
    return -999;
}

void MakeTree(int start, int end, int rootVal, int rootIdx)
{

    if (start &gt; end)
    {
        return;
    }

    int rootV = -999;
    int postRootIdx = PostOrderIdx[rootVal];

    cout &lt;&lt; rootVal &lt;&lt; &quot; &quot;;

    if ((rootV = FindLeftTreeRoot(rootIdx, postRootIdx, end)) != -999)
    {
        MakeTree(start, rootIdx - 1, rootV, InOrderIdx[rootV]);
    }

    if ((rootV = FindRightTreeRoot(rootIdx, postRootIdx)) != -999)
    {

        MakeTree(rootIdx + 1, end, rootV, InOrderIdx[rootV]);
    }

;

}



int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);

    cin &gt;&gt; N;

    for (int i = 1; i &lt;= N; i++)
    {
        cin &gt;&gt; InOrder[i];
        InOrderIdx[InOrder[i]] = i;    
    }

    for (int i = 1; i &lt;= N; i++)
    {
        cin &gt;&gt; PostOrder[i];
        PostOrderIdx[PostOrder[i]] = i;
    }

    int rootVal = PostOrder[N];

    MakeTree(1, N, rootVal,InOrderIdx[rootVal]);


    int a = 0;

    return 0;
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[키워드 및 STL 함수(C++)]]></title>
            <link>https://velog.io/@creat_my_world/%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%82%A4%EC%9B%8C%EB%93%9C-%EB%93%B1..C</link>
            <guid>https://velog.io/@creat_my_world/%EA%B0%84%EB%8B%A8%ED%95%9C-%ED%82%A4%EC%9B%8C%EB%93%9C-%EB%93%B1..C</guid>
            <pubDate>Fri, 03 Jan 2025 14:07:14 GMT</pubDate>
            <description><![CDATA[<h3 id="nodiscard-키워드">nodiscard 키워드</h3>
<p> 개발자가 함수 반환값을 반드시 사용해야 하는 상황에서 실수를 방지하도록 돕는 도구이다.</p>
<pre><code class="language-c++">[[nodiscard]] int GetID(const std::wstring&amp; userName)
{
    return DB.Get(userName);
}

int main()
{

    GetID(L&quot;Kim&quot;);//반환값이 사용되지 않음
    int my_ID = GetID(L&quot;Lee&quot;); // 반환값이 사용됨

}
</code></pre>
<p>이런 경우일 때 사용되고 추가로 </p>
<pre><code>[[nodiscard(&quot;사용자에 대한 ID를 사용하지 않고 있습니다!&quot;)]] 
int GetID(const std::wstring&amp; userName)
{
    return DB.Get(userName);
}
</code></pre><p>위와 같이 메시지를 추가하여 경고문으로 사용할 수 있다.</p>
<h3 id="noreturn">noreturn</h3>
<p> 함수가 반환하지 않을 것임을 컴파일러에 명시적으로 알리는 데 사용됩니다. 주로 프로그램 실행을 종료하거나, 무한 루프를 실행하거나, 예외를 던지는 함수에 적용된다.</p>
<p> 1.프로그램 종료함수</p>
<pre><code>#include &lt;cstdlib&gt;
#include &lt;stdexcept&gt;

[[noreturn]] void TerminateProgram() {
    std::exit(1); // 프로그램 종료
}

int main() {
    TerminateProgram();
    // 이 이후 코드는 절대 실행되지 않음
    return 0;
}
</code></pre><ol start="2">
<li>예외를 던지는 함수<pre><code>[[noreturn]] void ThrowError() {
 throw std::runtime_error(&quot;Error occurred!&quot;);
}
</code></pre></li>
</ol>
<p>void Process() {
    ThrowError();
    // 이 이후 코드는 절대 실행되지 않음
}</p>
<pre><code>
### string_view
✅ 1. std::string_view의 특징
1️⃣ 문자열을 복사하지 않음 → 기존 문자열 데이터를 참조하여 빠름
2️⃣ 읽기 전용 → 문자열을 수정할 수 없음 (std::string과 다름)
3️⃣ 자동으로 C-스타일 문자열(const char*)과 std::string을 참조 가능
4️⃣ 길이를 저장하고 있음 → strlen()보다 size() 호출이 빠름

✅ 2. 기본 사용법

```cpp
#include &lt;iostream&gt;
#include &lt;string_view&gt;

int main() {
    std::string_view sv = &quot;Hello, World!&quot;;  // 문자열 리터럴 참조
    std::cout &lt;&lt; sv &lt;&lt; std::endl;           // Hello, World!
    std::cout &lt;&lt; &quot;Length: &quot; &lt;&lt; sv.size() &lt;&lt; std::endl;  // 13
}</code></pre><p>✔ 문자열을 복사하지 않고 참조만 수행
✔ .size()는 O(1) 연산 (C-스타일 문자열의 strlen()은 O(n))</p>
<p>std::string_view는 std::string과 유사한 인터페이스를 제공하지만, 읽기 전용 함수만 사용 가능합니다.</p>
<p>함수    설명    예제
size()    문자열 길이 반환    sv.size()
empty()    문자열이 비어 있는지 확인    sv.empty()
substr(pos, len)    부분 문자열 반환 (std::string_view)    sv.substr(7, 5)
front()    첫 번째 문자 반환    sv.front()
back()    마지막 문자 반환    sv.back()
remove_prefix(n)    앞에서 n개 문자 제거    sv.remove_prefix(7)
remove_suffix(n)    뒤에서 n개 문자 제거    sv.remove_suffix(6)</p>
<p>유의 -&gt; 원본 문자열이 사라질 시 오류 발생</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Template 템플릿]]></title>
            <link>https://velog.io/@creat_my_world/Template-%ED%85%9C%ED%94%8C%EB%A6%BF</link>
            <guid>https://velog.io/@creat_my_world/Template-%ED%85%9C%ED%94%8C%EB%A6%BF</guid>
            <pubDate>Wed, 01 Jan 2025 15:34:48 GMT</pubDate>
            <description><![CDATA[<h2 id="template">Template</h2>
<p>사용자가 원하는 타입을 넣어주면 그에 맞게 짜여지는 틀과 같은 역할을 한다.</p>
<pre><code>template &lt;typename T&gt;
void print(T w)
{
    cout &lt;&lt; w &lt;&lt; endl;
}

int main()
{
    string s;
    cin &gt;&gt; s;

    print(s);

    int a;
    cin &gt;&gt; a;

    print(a);
}
</code></pre><pre><code>template &lt;typename T&gt;
class Print
{

public:

    Print()
    {
    }

    void pt()
    {
        std::cout &lt;&lt; typeid(T).name() &lt;&lt; std::endl;
    }



};

int main()
{

    Print&lt;std::string&gt; print_s;
    print_s.pt();
    Print&lt;int&gt; print_i;
    print_i.pt();


    return 0;
}</code></pre><p>위 코드처럼 들어가는 변수의 타입에 맞게 코드가 실행된다.</p>
<p>위 경우들 처럼 클래스 템플릿에 인자를 전달해서 실제 코드를 생성하는 것을 <strong>클래스 템플릿 인스턴스화 (class template instantiation)</strong>라고 한다.</p>
<h3 id="템플릿-특수화template-specializtion">템플릿 특수화(template specializtion)</h3>
<p>템플릿을 할 때 일부 경우에 대해선 따로 처리해주는 것이다.</p>
<pre><code>template &lt;typename T, typename A&gt;
class Print
{

public:

    Print()
    {
    }

    void pt()
    {
        std::cout &lt;&lt; typeid(T).name() &lt;&lt; std::endl;
    }



};

template &lt;&gt;
class Print&lt;int, float&gt;
{

public:

    Print()
    {
        std::cout &lt;&lt; &quot;float or int~~&quot; &lt;&lt; std::endl;
    }


};

template &lt;typename B&gt;
class Print&lt;std::string,B&gt;
{

public:

    Print()
    {
        std::cout &lt;&lt; &quot;string~~&quot; &lt;&lt; std::endl;
    }


};

int main()
{

    Print&lt;std::string, double&gt; print_s;
    Print&lt;int, float&gt; print_i;


    return 0;
}</code></pre><p>이런식으로 원래 선언한 템플릿의 개수에 맞추어 자신이 따로 처리하고 싶은 경우에 대하여 템플릿 특수화를 진행할 수 있다.</p>
<h2 id="함수-객체function-object--functor">함수 객체(Function object / Functor)</h2>
<pre><code>template&lt;typename T, typename Comp&gt;
void bubble_sort(T&amp; cont, Comp&amp; comp)
{
    for (int i = 0; i &lt; cont.size(); i++) 
    {
        for (int j = i + 1; j &lt; cont.size(); j++)
        {
            if (!comp(cont[i], cont[j])) 
            {
                int temp = cont[i];
                cont[i] = cont[j];
                cont[j] = temp;
            }
        }
    }
}

bool comp1(int a, int b)
{
    return (a &gt; b);
}

bool comp2(int a, int b)
{
    return (a &lt; b);
}


int main()
{
    std::vector&lt;int&gt; v;

    v.push_back(1);
    v.push_back(4);
    v.push_back(2);
    v.push_back(6);
    v.push_back(3);

    bubble_sort(v, comp1);

    for (int i = 0; i &lt; v.size(); i++)
    {
        std::cout &lt;&lt; v[i] &lt;&lt; std::endl;
    }</code></pre><p>코드를 입력하세요</p>
<pre><code>    bubble_sort(v, comp2);

    for (int i = 0; i &lt; v.size(); i++)
    {
        std::cout &lt;&lt; v[i] &lt;&lt; std::endl;
    }

    return 0;
}</code></pre><p>함수 객체가 없었다면 오름차순, 내림차순 정렬에 대하여 만들어줘야 하지만(함수포인터를 쓸 필요없이) 사용자가 원하는 조건에 대한 클래스를 만들어 주어서 실행할 수 있다는 점에서 더 간편하고 효율적이다.</p>
<h3 id="타입이-아닌-템플릿-인자-non-type-template-arguments">타입이 아닌 템플릿 인자 (non-type template arguments)</h3>
<pre><code>template&lt;typename T, int num&gt;
void print(T s)
{
    std::cout &lt;&lt; s &lt;&lt; &quot; &quot; &lt;&lt; num &lt;&lt; std::endl;
}

int main()
{
    std::string s;
    std::cin &gt;&gt; s;

    print&lt;std::string, 5&gt;(s);

    return 0;
}</code></pre><p>이와 같이 템플릿에 전달하는 인자가 타입이 아닌 정수와 같은 변수도 가능하다. 정수 이외에도(c++ 20부터) 밑에 있는 여러 자료형들도 사용이 가능해졌다. -&gt;컴파일 타임에 값들이 정해져야 하는 것에 많이 사용 
ex)STL의 array 경우 std::array&lt;int, 2&gt; arr 이런 식으로 미리 크기를 알려줄 수 있다.</p>
<ol>
<li><p>정수 타입들 (bool, char, int, long 등등). 당연히 float 과 double 은 제외</p>
</li>
<li><p>포인터 타입</p>
</li>
<li><p>enum 타입</p>
</li>
<li><p>std::nullptr_t (널 포인터)</p>
</li>
</ol>
<p>만약 아무 값도 전달해주지 않으면 컴파일 오류가 생긴다.</p>
<p>이를 방지하기 위해 </p>
<h3 id="디폴트-템플릿-인자">디폴트 템플릿 인자</h3>
<p>미리 디폴트 값을 정해두어 아무 값도 전달하지 않아도 실행될 수 있게 할 수 있다.</p>
<pre><code>template&lt;typename T, int num = 5&gt;
void print(T s)
{
    std::cout &lt;&lt; s &lt;&lt; &quot; &quot; &lt;&lt; num &lt;&lt; std::endl;
}

int main()
{
    std::string s;
    std::cin &gt;&gt; s;

    print&lt;std::string&gt;(s);

    return 0;
}</code></pre><p>타입 또한 디폴트로 전달이 가능하다.
최소값을 구하는 함수를 만들 때</p>
<pre><code>template&lt;typename T&gt;
struct Compare
{
    bool operator()(const T&amp; a, const  T&amp; b) const { return a &lt; b; }
};

template&lt;typename T, typename Comp = Compare&lt;T&gt;&gt;//디폴트로 설정해준 타입에 따라서 클래스가 실행됨
T Min(T a, T b) {
    Comp comp;
    if (comp(a, b)) {
        return a;
    }
    return b;
}



int main()
{
    int a = 3, b = 5;
    std::cout &lt;&lt; &quot;Min &quot; &lt;&lt; a &lt;&lt; &quot; , &quot; &lt;&lt; b &lt;&lt; &quot; :: &quot; &lt;&lt; Min(a, b) &lt;&lt; std::endl;

    std::string s1 = &quot;abc&quot;, s2 = &quot;def&quot;;
    std::cout &lt;&lt; &quot;Min &quot; &lt;&lt; s1 &lt;&lt; &quot; , &quot; &lt;&lt; s2 &lt;&lt; &quot; :: &quot; &lt;&lt; Min(s1, s2)
        &lt;&lt; std::endl;

    return 0;
}</code></pre><p>&lt;출처&gt;</p>
<p><a href="https://modoocode.com/219">모두의 코드 c++</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[7. Rasterizer, Depth/ Stencil, Ble]]></title>
            <link>https://velog.io/@creat_my_world/7.-Rasterizer-Depth-Stencil-Ble</link>
            <guid>https://velog.io/@creat_my_world/7.-Rasterizer-Depth-Stencil-Ble</guid>
            <pubDate>Wed, 01 Jan 2025 12:46:43 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@creat_my_world/7%EA%B0%95-Rasterizer">레스터라이져 설명</a></p>
<h2 id="레스터라이져-desc">레스터라이져 Desc</h2>
<pre><code class="language-c++">//  래스터라이저 스테이트 오브젝트 생성

D3D11_RASTERIZER_DESC RSDesc;
RSDesc.FillMode = D3D11_FILL_SOLID;           // 평범하게 렌더링
RSDesc.CullMode = D3D11_CULL_NONE;         // 컬모드 하지 않음
RSDesc.FrontCounterClockwise = FALSE;        // 시계방향이 뒷면임 CCW
RSDesc.DepthBias             = 0;                      //깊이 바이어스 값 0
RSDesc.DepthBiasClamp        = 0;
RSDesc.SlopeScaledDepthBias  = 0;
RSDesc.DepthClipEnable       = FALSE;            // 깊이 클리핑 없음
RSDesc.ScissorEnable         = FALSE;             // 시저 테스트 하지 않음
RSDesc.MultisampleEnable     = FALSE;          // 멀티 샘플링 하지 않음
RSDesc.AntialiasedLineEnable = FALSE;         // 라인 안티앨리어싱 없음

hr = g_pD3DDevice-&gt;CreateRasterizerState(&amp;RSDesc, &amp;g_pRasterizerState);</code></pre>
<p><a href="https://learn.microsoft.com/ko-kr/windows/win32/api/d3d11/ns-d3d11-d3d11_rasterizer_desc">Desc와 모드에 관한 더 상세한 설명</a></p>
<h2 id="blend">Blend</h2>
<p><a href="https://velog.io/@creat_my_world/10%EC%9E%A5-OutMerger">output merger에 대한 설명(깊이,스텐실,블렌딩)</a></p>
<p>Blend Desc</p>
<pre><code>        D3D11_BLEND_DESC bsDesc = {};
        bsDesc.AlphaToCoverageEnable = false; //멀티 샘플링에서 알파값 사용 유무
        bsDesc.IndependentBlendEnable = false; // False -&gt; 렌더타겟의 0번쨰만 사용 (1~7 번째는 무시)

        // 0번 렌더타겟에 대한 블렌드 상태 설정 (0~7 번까지 설정 가능하다)
        bsDesc.RenderTarget[0].BlendEnable = true; 
        bsDesc.RenderTarget[0].SrcBlend = D3D11_BLEND::D3D11_BLEND_SRC_ALPHA;
        bsDesc.RenderTarget[0].DestBlend = D3D11_BLEND::D3D11_BLEND_INV_SRC_ALPHA;
        bsDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP::D3D11_BLEND_OP_ADD;
        bsDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND::D3D11_BLEND_ONE;
        bsDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND::D3D11_BLEND_ZERO;
        bsDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP::D3D11_BLEND_OP_ADD;
        bsDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE::D3D11_COLOR_WRITE_ENABLE_ALL;
        GetDevice()-&gt;CreateBlendState(&amp;bsDesc, blendStates[(UINT)eBlendState::AlphaBlend].GetAddressOf());</code></pre><p><a href="https://learn.microsoft.com/ko-kr/windows/win32/api/d3d11/ns-d3d11-d3d11_render_target_blend_desc">Blend Desc에 관한 상세한 설명</a></p>
<p>Depth/Stencil Desc</p>
<pre><code>ID3D11DepthStencilState* g_pDepthStencilState = NULL;

D3D11_DEPTH_STENCIL_DESC DepthStencilDesc;

DepthStencilDesc.DepthEnable = TRUE;                                    //깊이 테스트 활성화
DepthStencilDesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;            //쓰기
DepthStencilDesc.DepthFunc  = D3D11_COMPARISON_LESS;                     // 앞쪽물체를 렌더링
DepthStencilDesc.StencilEnable = FALSE;                                  //스텐실 테스트 비활성화
DepthStencilDesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;      //스텐실 읽기 마스크
DepthStencilDesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;   //스텐실 쓰기 마스크

// 면이 앞을 보고 있을 경우 스텐실 테스트 설정
DepthStencilDesc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;        //유지
DepthStencilDesc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;   //유지
DepthStencilDesc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;        //유지
DepthStencilDesc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;        //항상 성공

//면이 뒤를 보고 있을경우 스텐실 테스트 설정
DepthStencilDesc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP;         //유지
DepthStencilDesc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_KEEP;    //유지
DepthStencilDesc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;         //유지
DepthStencilDesc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS;         //항상 성공

g_pd3dDevice-&gt;CreateDepthStencilState(&amp;DepthStencilDesc,&amp;g_pDepthStencilState);</code></pre><p><a href="https://learn.microsoft.com/en-us/windows/win32/api/d3d11/ns-d3d11-d3d11_depth_stencil_desc">Depth/Stencil Desc 상세 설명</a></p>
<p>&lt;참고자료&gt;
<a href="https://www.yamyamcoding.com/dcef3d70-d2b8-41f8-848d-db90482d432b">얌얌 코딩 수업노트</a></p>
]]></description>
        </item>
    </channel>
</rss>