<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>웅웅이의 지식창고</title>
    <link>https://jungwoong.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 6 May 2026 21:33:31 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>웅웅이님</managingEditor>
    <image>
      <title>웅웅이의 지식창고</title>
      <url>https://tistory1.daumcdn.net/tistory/3288360/attach/7dfadf6a18d64246960a085d0123807e</url>
      <link>https://jungwoong.tistory.com</link>
    </image>
    <item>
      <title>[c++] 반환값 최적화 (RVO, return-value-optimization)</title>
      <link>https://jungwoong.tistory.com/108</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;c++ 컴파일러는 빠른 속도를 제공해주기 위해서 다양한 최적화를 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그중에 이번 글에서는 반환값 최적화에 대해서 알아보며 내부 동작을 이해하는 시간을 가져 봅니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727483867830&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;string&amp;gt;
class CTest
{
public:
	CTest()
	{
		std::cout &amp;lt;&amp;lt; &quot;CTest Constructor, value : &quot; &amp;lt;&amp;lt; m_str.c_str() &amp;lt;&amp;lt; std::endl;
	}

	CTest(const std::string &amp;amp; str) : m_str(str)
	{
		std::cout &amp;lt;&amp;lt; &quot;CTest Constructor strParam, value : &quot; &amp;lt;&amp;lt; m_str.c_str() &amp;lt;&amp;lt; std::endl;
	}

	CTest(const CTest &amp;amp; t)
	{
		this-&amp;gt;m_str = t.m_str;
		std::cout &amp;lt;&amp;lt; &quot;CTest Copy Constructor, value : &quot; &amp;lt;&amp;lt; m_str.c_str() &amp;lt;&amp;lt; std::endl;
	}

	CTest(CTest&amp;amp;&amp;amp; t) noexcept
	{
		this-&amp;gt;m_str = std::move(t.m_str);
		std::cout &amp;lt;&amp;lt; &quot;CTest Move Constructor, value : &quot; &amp;lt;&amp;lt; m_str.c_str() &amp;lt;&amp;lt; std::endl;
	}

	~CTest()
	{
		std::cout &amp;lt;&amp;lt; &quot;CTest Destructor, value : &quot; &amp;lt;&amp;lt; m_str.c_str() &amp;lt;&amp;lt; std::endl;
	}

public:
	std::string m_str;
};

class CTestRapper
{
public:
	CTestRapper(const CTest&amp;amp; ct)
	{
		std::cout &amp;lt;&amp;lt; &quot;CTestRapper Constructor&quot; &amp;lt;&amp;lt; std::endl;
	}
	~CTestRapper()
	{
		std::cout &amp;lt;&amp;lt; &quot;CTestRapper Destructor&quot; &amp;lt;&amp;lt; std::endl;
	}
};

// RVO(Return Value Optimization) 예제
CTest CallRVO()
{
	std::cout &amp;lt;&amp;lt; &quot;CALL CallRVO Function&quot; &amp;lt;&amp;lt; std::endl;
	return CTest(&quot;RVO&quot;);
}

// NRVO(Named Return Value Optimization) 예제
CTest CallNRVO()
{
	std::cout &amp;lt;&amp;lt; &quot;CALL CallNRVO Function&quot; &amp;lt;&amp;lt; std::endl;
	CTest tempCTest(&quot;NRVO&quot;);
	return tempCTest;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RVO 동작 방식을 알아 보기 위해서 위와 같은 예제 코드를 준비 하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;함수의 반환 되는 임시 객체에 std::move 적용&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;반환값 최적화를 공부하기 전에 Move Sementics를 처음 배우게 되었을 때 시절의 이야기를 해보려고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;다들 그 당시 다음과 같은 상상을 해본적이 있을 것 같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&quot;함수 안에서 생성되는 임시 객체에 대한 리턴시 복사 생성이 되고 있으니 해당 로직을&lt;span&gt;&amp;nbsp;&lt;/span&gt;이동 연산을 적용하자.&quot;&lt;/p&gt;
&lt;pre id=&quot;code_1727519724038&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CTest &amp;amp;&amp;amp; CallReturnMove()
{
	std::cout &amp;lt;&amp;lt; &quot;CALL CallReturnMove Function&quot; &amp;lt;&amp;lt; std::endl;
	CTest tempCTest(&quot;NRVO&quot;);
	return std::move(tempCTest);
}

int main()
{
	CTest ct = CallReturnMove();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 로직을 실행 시켜보면 우리가 예상한대로 호출이 되며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CTest의 m_str 객체가 이동연산이 되면 복사 연산보다 더욱 효율적으로&amp;nbsp;동작하는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 아쉽게도 해당 로직이 내부적으로 컴파일러에게 잘못된 내용을 전달하여 최적화 수행을 방해하게 되며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;warning C4172: 지역 변수 또는 임시: tempCTest의 주소를 반환하고 있습니다.&lt;/b&gt;&quot; 같은 warning을 호출 하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;113&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOFZht/btsJPQM0bin/afbS8BT1dXgGmDKu6WGy21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOFZht/btsJPQM0bin/afbS8BT1dXgGmDKu6WGy21/img.png&quot; data-alt=&quot;move를 사용한 임시값 반환&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOFZht/btsJPQM0bin/afbS8BT1dXgGmDKu6WGy21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOFZht%2FbtsJPQM0bin%2FafbS8BT1dXgGmDKu6WGy21%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;518&quot; height=&quot;152&quot; data-origin-width=&quot;385&quot; data-origin-height=&quot;113&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;move를 사용한 임시값 반환&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;RVO 동작 호출&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RVO란 return value optimization으로&amp;nbsp; CallRVO 형태 처럼 return 값을 rvalue로 호출 할 때 동작하는 최적화 기법을 말합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1727483992479&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// RVO(Return Value Optimization) 예제
CTest CallRVO()
{
	std::cout &amp;lt;&amp;lt; &quot;CALL CallRVO Function&quot; &amp;lt;&amp;lt; std::endl;
	return CTest(&quot;RVO&quot;);
}

/// 생략...

int main()
{
	CTest Ct1 = CallRVO();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 실행 시키면 &quot;CTest의 생성자 및 소멸자는 몇 번 호출되어야 할까요?&quot;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드로 파악해보면 다음과 같은 결과로 출력되어야 할 것으로 예상됩니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CallRVO에서 CTest의&amp;nbsp; 생성자 호출,&amp;nbsp; 소멸자 호출(Ct1의 복사 생성 완료 후)&lt;/li&gt;
&lt;li&gt;main 함수에서는 CTest의 복사 생성자 호출 및 소멸자 호출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 실제로 실행해보면 다음과 같은 결과를 얻는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복사 생성자가 호출 되지 않고 있으며 소멸자도 1회만 호출 되고 있습니다. 놀라운 결과 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;81&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Au19e/btsJQIUAK3b/ag47qoy9mT1CQ5FNQW0lfK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Au19e/btsJQIUAK3b/ag47qoy9mT1CQ5FNQW0lfK/img.png&quot; data-alt=&quot;RVO 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Au19e/btsJQIUAK3b/ag47qoy9mT1CQ5FNQW0lfK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAu19e%2FbtsJQIUAK3b%2Fag47qoy9mT1CQ5FNQW0lfK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;106&quot; data-origin-width=&quot;418&quot; data-origin-height=&quot;81&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RVO 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 디버깅으로 확인 해보면 다음의 위치에 생성자 및 소멸자가 호출 되는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CallRVO 함수의 &quot;return CTest(&quot;RVO&quot;);&quot; 구문에서 CTest의 생성자 호출&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Main함수의 중괄호가 끝나는 지점에서 CTest의 소멸자 호출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CallRVO 함수에서 &lt;b&gt;Main함수의 &quot;CTest Ct1&quot; 객체 메모리 주소에 객체를 생성&lt;/b&gt;하는 방식으로 최적화가 동작하고 있는 것으로 보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러는 매우 똑똑하게 객체의 생성을 최소화 하는 형식으로 최적화 되고 있네요. 든든합니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;리턴 값이 일치하지 않은 호출&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 자세하게 알아보기 위해서 Main에서 CTestRapper를 통해서 CallRVO함수를 결과를 저장하도록 실행해보면 명확하게 알 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727484633637&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int main()
{
    CTestRapper CtRapper1 = CallRVO();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CTestRapper를 사용하면 함수의 리턴 값과 복사생성되는 객체의 타입이 일치 하지 않아 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;최적화 로직이 실행 되지 않습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt; 코드상으로 보이는 대로 호출 되는 것을 확인 할 수 있습니다. (&lt;/span&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;컴파일러의 최적화 기준에 도달하지 못한 것 같네요..)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CallRVO에서 CTest의&amp;nbsp; 생성자 호출, 소멸자 호출(CtRapper1 생성자 호출 완료 후)&lt;/li&gt;
&lt;li&gt;main 함수에서는 CTestRapper의 생성자 호출 및 소멸자 호출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;116&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZRWJn/btsJPzx4MhP/NgNukW9rOKwYKTehy64Il1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZRWJn/btsJPzx4MhP/NgNukW9rOKwYKTehy64Il1/img.png&quot; data-alt=&quot;RVO가 실행되지 않았을때 동작&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZRWJn/btsJPzx4MhP/NgNukW9rOKwYKTehy64Il1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZRWJn%2FbtsJPzx4MhP%2FNgNukW9rOKwYKTehy64Il1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;513&quot; height=&quot;143&quot; data-origin-width=&quot;416&quot; data-origin-height=&quot;116&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RVO가 실행되지 않았을때 동작&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;NRVO 동작 호출&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NRVO란 named return value optimization으로&amp;nbsp; &lt;b&gt;CallNRVO&lt;/b&gt; 형태 처럼 return 값을 rvalue로 호출 할 때 동작하는 최적화 기법을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RVO와는 다르게 이름을 가진 객체를 리턴하도록 구현하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1727518414967&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 생략...

// NRVO(Named Return Value Optimization) 예제
CTest CallNRVO()
{
	std::cout &amp;lt;&amp;lt; &quot;CALL CallNRVO Function&quot; &amp;lt;&amp;lt; std::endl;
	CTest tempCTest(&quot;NRVO&quot;);
	return tempCTest;
}

int main()
{
	CTest ct2 = CallNRVO();
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;코드로 파악해보면 다음과 같은 결과로 출력되어야 할 것으로 예상됩니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CallNRVO에서 CTest의&amp;nbsp; 생성자 호출,&amp;nbsp; 소멸자 호출(Ct1의 복사 생성 완료 후)&lt;/li&gt;
&lt;li&gt;main 함수에서는 CTest의 복사 생성자 호출 및 소멸자 호출&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 실제로 실행해보면 다음과 같은 결과를 얻는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;디버그 환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;디버그 환경에서는 반환값을 rvalue처럼 판단하여 move 연산을 수행해서 최적화 합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;107&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8ezAz/btsJO8OstDp/1Tr9sSAadG3rC9lVThADQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8ezAz/btsJO8OstDp/1Tr9sSAadG3rC9lVThADQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8ezAz/btsJO8OstDp/1Tr9sSAadG3rC9lVThADQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8ezAz%2FbtsJO8OstDp%2F1Tr9sSAadG3rC9lVThADQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;150&quot; data-origin-width=&quot;382&quot; data-origin-height=&quot;107&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;릴리즈 환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;릴리즈 환경에서는 RVO와 같이 동작하는 것을 볼 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4mQ4e/btsJQ2FvtNW/Y774skJvGeAbstO4RBFZe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4mQ4e/btsJQ2FvtNW/Y774skJvGeAbstO4RBFZe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4mQ4e/btsJQ2FvtNW/Y774skJvGeAbstO4RBFZe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4mQ4e%2FbtsJQ2FvtNW%2FY774skJvGeAbstO4RBFZe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;118&quot; data-origin-width=&quot;430&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RVO 호출과 같이 컴파일러가 매우 똑똑하게 NRVO를 최적화 해주고 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;축하합니다. 앞으로 우리는 이러한 구문을 보았을 때 똑똑한 컴파일러에게 감사함을 느끼며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최적화를 믿고 맏길 수 있게 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;긴 글을 읽어 주셔서 감사합니다. ( _ _ )&lt;/p&gt;</description>
      <category>c++/c++</category>
      <category>C++</category>
      <category>c++최적화</category>
      <category>named return value optimization</category>
      <category>nrvo</category>
      <category>return value optimization</category>
      <category>RVO</category>
      <category>std::move</category>
      <category>반환값 최적화</category>
      <category>반환값최적화</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/108</guid>
      <comments>https://jungwoong.tistory.com/108#entry108comment</comments>
      <pubDate>Thu, 26 Sep 2024 14:50:05 +0900</pubDate>
    </item>
    <item>
      <title>[c++] find_if</title>
      <link>https://jungwoong.tistory.com/107</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;find_if는 전달된 범위에서 지정된 조건을 만족하는 첫번째 요소를 리턴해줍니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;함수 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수의 정의를 보시면 순차 검색을 통해서 조건에 만족하는 iterator를 리턴합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726318429090&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 자세한 구현 사항은 컴파일러를 통해서 정의된 구문을 참조 바랍니다. 
template&amp;lt;class _InIt, class _Pr&amp;gt;
_InIt find_if(_InIt _First, const _InIt _Last, _Pr _Pred) { 
    for (; _First != _Last; ++_First) {
        if (_Pred(*_First)) 
            break;        
    }
    return _First;
}&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.2326%;&quot;&gt;매개변수&lt;/td&gt;
&lt;td style=&quot;width: 84.7674%;&quot;&gt;설명&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.2326%;&quot;&gt;_First&lt;/td&gt;
&lt;td style=&quot;width: 84.7674%;&quot;&gt;검색할 범위의 첫번째 요소의 iterator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.2326%;&quot;&gt;_Last&lt;/td&gt;
&lt;td style=&quot;width: 84.7674%;&quot;&gt;검색할 범위의 마지막 요소의 iterator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 15.2326%;&quot;&gt;_Pred&lt;/td&gt;
&lt;td style=&quot;width: 84.7674%;&quot;&gt;검색 조건을 정의하는 사용자 정의 함수 객체입니다.&amp;nbsp;&lt;br /&gt;bool(const T&amp;amp; arg)의 형식을 갖춰야 합니다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기본 사용 구문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예제는 컨테이너에서 5를 초과하는 요소를 찾는 간단한 예제 구문입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726319081005&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

int main()
{
	vector&amp;lt;int&amp;gt; ints = { 1,2,3,4,5,6,7,8,9,10 };

	// 5 초과 값을 가진 요소를 찾으시오
	const auto findIt = find_if(begin(ints), end(ints), [](int value) { return 5 &amp;lt; value;  });

	std::cout &amp;lt;&amp;lt; &quot;findIt : &quot; &amp;lt;&amp;lt; *findIt &amp;lt;&amp;lt; std::endl; 

	return 0;
}

// 출력 : 6&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;응용 사용 구문&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 find_if를 실무에서 발생할 수 있는 간단한 사례를 들어서 설명 드립니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;주어진 AbilityTicket에서 weight 기반으로 랜덤 뽑기를 구현해주세요. &quot;&amp;nbsp; 다음과 같은 조건으로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 요청이 들어 온 경우 컨테이너에서 weight를 검사하여 찾는 구문에 find_if를 사용할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1726322514135&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;algorithm&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;iostream&amp;gt;

using namespace std;

struct AbilityTicket
{
	AbilityTicket(const std::string&amp;amp; name, uint16_t value, uint16_t weight)
		: name(name), value(value), weight(weight)
	{}

	std::string name;					// 이름
	uint16_t	value;					// 값		
	uint16_t	weight;					// 비중
};

vector&amp;lt;AbilityTicket&amp;gt; AbilityLotteryContainer = {
	{&quot;attack&quot;, 5, 5},
	{&quot;defense&quot;, 5, 5},
	{&quot;critical&quot;, 2, 5},
	{&quot;reduction&quot;, 3, 3},
	{&quot;hp&quot;, 100, 10},
	{&quot;mp&quot;, 50, 10}
};

// 리턴값 : 0 ~ (base - 1), 간략한 랜덤 함수
const int getRandomValue(const int base)
{
	return rand() % base;
}

int main()
{
	srand((unsigned int)time(nullptr));

	// 요청 : AbilityLotteryContainer에서 weight를 기반으로 랜덤 옵션 하나를 뽑아주세요
	uint32_t totalWeight{ 0 };
	std::vector&amp;lt;uint32_t&amp;gt; accumulateWeights;
	accumulateWeights.reserve(AbilityLotteryContainer.size());
    
	// ability별 가중도를 accumulateWeights 추가 합니다. 
	for (auto&amp;amp; abilityTicket : AbilityLotteryContainer)
	{
		totalWeight += abilityTicket.weight;
		accumulateWeights.push_back(totalWeight);
	}

	// 랜덤 값 추출
	const auto lot{ static_cast&amp;lt;uint32_t&amp;gt;(getRandomValue(totalWeight)) };

	// lot 값을 기반으로 ability를 찾습니다.(이곳에서 find_if를 사용합니다.)
	const auto findIt = std::find_if(begin(accumulateWeights), end(accumulateWeights),
		[lot](const auto &amp;amp; accumulateWeight)
		{ return lot &amp;lt; accumulateWeight; });

	// 같은 기능을 lower_bound를 통해서 구현 할 수도 있습니다. 
	// const auto findIt2 = std::lower_bound(begin(accumulateWeights), end(accumulateWeights), lot + 1);

	// 값을 찾지 못하였는지 체크 
	if (findIt == end(accumulateWeights)) {
		std::cout &amp;lt;&amp;lt; &quot;not find&quot; &amp;lt;&amp;lt; '\n';
		return 0;
	}

	// 찾은 인덱스를 구합니다. 
	const auto index = findIt - begin(accumulateWeights);

	std::cout &amp;lt;&amp;lt; &quot;lot : &quot; &amp;lt;&amp;lt; lot &amp;lt;&amp;lt; '\n';
	std::cout &amp;lt;&amp;lt; &quot;find Ability : &quot; &amp;lt;&amp;lt; AbilityLotteryContainer[index].name &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; AbilityLotteryContainer[index].value &amp;lt;&amp;lt; &quot; index : &quot; &amp;lt;&amp;lt; index &amp;lt;&amp;lt; '\n';

	return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 정렬되어 있다면 비슷한 기능을 lower_bound 를 통해서도 구현 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lower_bound는 이진 탐색으로 구현되기에 검색 데이터가 많다면 find_if보다 빠른 검색 속도를 제공합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능차이가 궁금해서 제 로컬 환경에서 테스트 했을 때 결과를 공유 드리면 다음과 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Release 빌드, 최적화를 피하기 위해서 결과 데이터 저장&amp;nbsp;&lt;/li&gt;
&lt;li&gt;대략적인 성능 측정 및 테스트 도중에 오차가 있을수 있음을 감안 부탁 드립니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 88px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 15.3489%; height: 20px;&quot;&gt;Ticket 갯수&lt;/td&gt;
&lt;td style=&quot;width: 84.6511%; height: 20px;&quot;&gt;성능차이( find_if와 lower_bound의 성능 측정)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.3489%; height: 17px;&quot;&gt;100개&lt;/td&gt;
&lt;td style=&quot;width: 84.6511%; height: 17px;&quot;&gt;약 2배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.3489%; height: 17px;&quot;&gt;300개&lt;/td&gt;
&lt;td style=&quot;width: 84.6511%; height: 17px;&quot;&gt;약 3배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.3489%; height: 17px;&quot;&gt;600개&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 84.6511%; height: 17px;&quot;&gt;약 6배&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 15.3489%; height: 17px;&quot;&gt;1200개&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 84.6511%; height: 17px;&quot;&gt;약 10배&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/algorithm/find&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://en.cppreference.com/w/cpp/algorithm/find&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1726317460553&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;std::find, std::find_if, std::find_if_not - cppreference.com&quot; data-og-description=&quot;(1) template&amp;lt; class InputIt, class T &amp;gt; InputIt find( InputIt first, InputIt last, const T&amp;amp; value ); (constexpr since C++20) (until C++26) template&amp;lt; class InputIt, class T = typename std::iterator_traits &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&quot; data-og-host=&quot;en.cppreference.com&quot; data-og-source-url=&quot;https://en.cppreference.com/w/cpp/algorithm/find&quot; data-og-url=&quot;https://en.cppreference.com/w/cpp/algorithm/find&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/algorithm/find&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.cppreference.com/w/cpp/algorithm/find&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;std::find, std::find_if, std::find_if_not - cppreference.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;(1) template&amp;lt; class InputIt, class T &amp;gt; InputIt find( InputIt first, InputIt last, const T&amp;amp; value ); (constexpr since C++20) (until C++26) template&amp;lt; class InputIt, class T = typename std::iterator_traits &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.cppreference.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>c++/c++</category>
      <category>Algorithm</category>
      <category>C++</category>
      <category>find_if</category>
      <category>STL</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/107</guid>
      <comments>https://jungwoong.tistory.com/107#entry107comment</comments>
      <pubDate>Sat, 14 Sep 2024 23:03:00 +0900</pubDate>
    </item>
    <item>
      <title>[설치] SQL Server 2019 Developer 설치하기</title>
      <link>https://jungwoong.tistory.com/106</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;개발 및 테스트를 위한 무료 버전인 SQL Server 2019를 설치하는 방법에 대해서 알아 봅니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.microsoft.com/ko-kr/sql-server/sql-server-downloads&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.microsoft.com/ko-kr/sql-server/sql-server-downloads&lt;/a&gt; 경로에서 설치 파일을 다운 받습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3dIZu/btrKF0PhreA/xPCDMufWTJOhLPzsVBqeik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3dIZu/btrKF0PhreA/xPCDMufWTJOhLPzsVBqeik/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3dIZu/btrKF0PhreA/xPCDMufWTJOhLPzsVBqeik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3dIZu%2FbtrKF0PhreA%2FxPCDMufWTJOhLPzsVBqeik%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;690&quot; height=&quot;345&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운 받은 파일을 클릭하면 다음과 같은 화면이 나오게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfEIhF/btrKGPmb5zJ/oYt2wBwM0aNojLHswRIgvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfEIhF/btrKGPmb5zJ/oYt2wBwM0aNojLHswRIgvK/img.png&quot; data-alt=&quot;SQL 설치&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfEIhF/btrKGPmb5zJ/oYt2wBwM0aNojLHswRIgvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfEIhF%2FbtrKGPmb5zJ%2FoYt2wBwM0aNojLHswRIgvK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;491&quot; data-origin-width=&quot;847&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQL 설치&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 : 기본 구성으로 SQL Server 설치&lt;/li&gt;
&lt;li&gt;사용자 지정 : 사용자가 지정한대로 SQL Server 설치&lt;/li&gt;
&lt;li&gt;미디어 다운로드 : 설치 프로그램을 ISO 또는 CAB 파일로 다운로드 받습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능 선택 탭에서는 필요한 기능들을 선택 하여 설치 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;605&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hpfme/btrKGOHBvyq/gMQE0WSk1mQVINiSTBxZ01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hpfme/btrKGOHBvyq/gMQE0WSk1mQVINiSTBxZ01/img.png&quot; data-alt=&quot;SQL&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hpfme/btrKGOHBvyq/gMQE0WSk1mQVINiSTBxZ01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHpfme%2FbtrKGOHBvyq%2FgMQE0WSk1mQVINiSTBxZ01%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;605&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;605&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SQL&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 베이스 엔진 구성 탭에서는 인증 모드 방식 및 DB의 데이터 저장 디렉토리, 메모리 설정을 수행할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증 모드에 대해서 간단히 설명을 드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Windows 인증 모드는 윈도우 사용자를 통해서 SQL Server에 접근하는 인증 방식입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL Server 인증 모드는 SQL Server의 사용자를 통해서 SQL Server 접근하는 인증 방식입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 설정에서 혼합 모드를 사용하지 않으면 윈도우 계정을 통해서 접근 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;348&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Vph78/btrKFFStzsG/SFqEw7VqKgmpqCj4kiDzIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Vph78/btrKFFStzsG/SFqEw7VqKgmpqCj4kiDzIK/img.png&quot; data-alt=&quot;데이터 베이스 엔진 구성 탭&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Vph78/btrKFFStzsG/SFqEw7VqKgmpqCj4kiDzIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVph78%2FbtrKFFStzsG%2FSFqEw7VqKgmpqCj4kiDzIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;348&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;348&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;데이터 베이스 엔진 구성 탭&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 완료되면 다음과 같은 화면이 나오게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yDz8L/btrKGooVxdw/K87e08BTR7HapcF9jQVfu1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yDz8L/btrKGooVxdw/K87e08BTR7HapcF9jQVfu1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yDz8L/btrKGooVxdw/K87e08BTR7HapcF9jQVfu1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyDz8L%2FbtrKGooVxdw%2FK87e08BTR7HapcF9jQVfu1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;398&quot; data-origin-width=&quot;805&quot; data-origin-height=&quot;398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우의 서비스 탭을 확인해보면 다음과 같이 설치되어 있는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8Z80z/btrKFFEVx3F/Jg430KsLG0DrTfcabNr7x0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8Z80z/btrKFFEVx3F/Jg430KsLG0DrTfcabNr7x0/img.png&quot; data-alt=&quot;서비스 설치 완료&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8Z80z/btrKFFEVx3F/Jg430KsLG0DrTfcabNr7x0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8Z80z%2FbtrKFFEVx3F%2FJg430KsLG0DrTfcabNr7x0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;88&quot; data-origin-width=&quot;772&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;서비스 설치 완료&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>MSSQL</category>
      <category>developer</category>
      <category>SQL Server</category>
      <category>SQL Server 2019</category>
      <category>SQL Server developer</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/106</guid>
      <comments>https://jungwoong.tistory.com/106#entry106comment</comments>
      <pubDate>Fri, 26 Aug 2022 22:54:45 +0900</pubDate>
    </item>
    <item>
      <title>[c++17] fold expression(폴드 표현식)</title>
      <link>https://jungwoong.tistory.com/103</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;c++11부터 parameter pack라는 기능이 제공되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;parameter pack을 사용하면 0개 이상의 템플릿 인자를 사용할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제처럼 템플릿 파라미터를 &quot;typename... Targs&quot; 형식으로 선언하여 가변 인자를 처리 하도록 구성 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655902413351&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

template&amp;lt;typename T&amp;gt;
constexpr auto tsum(T value) // 기본 함수
{
    return value;
}

template&amp;lt;typename T, typename... Targs&amp;gt;
constexpr auto tsum(T value, Targs... args) // 재귀 가변 함수
{
    return value + tsum(args...);
}


int main()
{
    constexpr auto sum = tsum(1, 2, 3, 4, 5, 6);
    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; '\n'; // 21
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++ 17부터는 fold expression이 제공되어서 parameter pack을 더욱 손쉽게 사용 할 수 있게 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fold expression 의 테스트 환경&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ENmtL/btrFrachMlR/5f7e2iRSUsZjhUvvIxwME0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ENmtL/btrFrachMlR/5f7e2iRSUsZjhUvvIxwME0/img.png&quot; data-alt=&quot;3중 비교 연산자 컴파일러 지원 옵션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ENmtL/btrFrachMlR/5f7e2iRSUsZjhUvvIxwME0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FENmtL%2FbtrFrachMlR%2F5f7e2iRSUsZjhUvvIxwME0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;221&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3중 비교 연산자 컴파일러 지원 옵션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Visual Studio 사용시 프로젝트 우 클릭 -&amp;gt; 구성 속성 -&amp;gt; 일반 -&amp;gt; c++ 언어 표준을 ISO C++ 17 이상으로 수정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/compiler-warnings-by-compiler-version?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VisualStudio 버전별 컴파일러 버전&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fold expression 사용 예제&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fold expression을 사용하면 위에서 설명드린 예제를 쉽게 처리 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655906949145&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

template&amp;lt;typename... Targs&amp;gt;
constexpr auto tsum(Targs... args) // 재귀 가변 함수
{
    return (args + ...); // 단항 오른쪽 fold
}

int main()
{
    constexpr auto sum = tsum(1, 2, 3, 4, 5, 6);
    cout &amp;lt;&amp;lt; sum &amp;lt;&amp;lt; '\n'; // 21

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fold expression 문법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fold expression은 4가지 문법으로 사용할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단항 오른쪽 fold&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 문법으로 사용 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;pack op ...&amp;nbsp;&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1655907194945&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename... Args&amp;gt;
auto sum(Args... args) { 
    return (args + ...); 
}

int main(){
    sum(1, 2, 3, 4);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단항 오른쪽 fold는 전달받은 parameter pack을 다음과 같은 표현식으로 변경 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;E1 op ( ... op ( En-1 op En))&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뒤에 있는 인자 부터 먼저 순차적으로 연산해서 결과 값을 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드는 위의 예제를 컴파일러가 어떻게 생성하는지 보여줍니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655907950083&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename... Args&amp;gt;
auto sum(Args... args) { 
    return (args + ...); 
}

#ifdef INSIGHTS_USE_TEMPLATE
template&amp;lt;&amp;gt;
int sum&amp;lt;int, int, int, int&amp;gt;(int __args0, int __args1, int __args2, int __args3)
{
    return __args0 + (__args1 + (__args2 + __args3));
}
#endif

int main()
{
    sum(1, 2, 3, 4);
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단항 왼쪽 fold&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&lt;b&gt;...&amp;nbsp; op &lt;/b&gt;pack&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1655908239912&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename... Args&amp;gt;
auto sum(Args... args) { 
    return (... + args); 
}

int main(){
    sum(1, 2, 3, 4);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 parameter pack을 다음과 같은 표현식으로 변경 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;((E1 op E2) op ...) op En&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에 있는 인자 부터 순차적으로 연산해서 결과 값을 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러가 생성한 단항 왼쪽 fold는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655908546777&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 생략...
template&amp;lt;&amp;gt;
int sum&amp;lt;int, int, int, int&amp;gt;(int __args0, int __args1, int __args2, int __args3)
{
    return ((__args0 + __args1) + __args2) + __args3;
}
// 생략...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이항 오른쪽 fold&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단항을 사용하는 문법과 다르게 parameter pack과 별개로 초기 연산할 값을 추가 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;pack op ... op init&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1655909242411&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename ...Args&amp;gt;
int sum(Args&amp;amp;&amp;amp;... args)
{
    return (args + ... + (1 * 2));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 parameter pack을 다음과 같은 표현식으로 변경 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;E1 op ( ... op ( En-1 op ( En op I )))&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;이항 왼쪽 fold&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;init&amp;nbsp;op ... op pack&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1655909290922&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename ...Args&amp;gt;
int sum(Args&amp;amp;&amp;amp;... args)
{
    return ((1 * 2) + ... + args);
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전달받은 parameter pack을 다음과 같은 표현식으로 변경 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;((( I op E1 ) op E2) op ...) op En&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단항 fold에 길이가 0인 parameter pack이 전달이 가능한 경우&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;&lt;b&gt;AND(&amp;amp;&amp;amp;)&lt;/b&gt;&lt;/i&gt; 연산자 인경우 true를 리턴 합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;i&gt;&lt;b&gt;OR(||)&lt;/b&gt; &lt;/i&gt;연산자인 경우 false를 리턴 합니다.&lt;/li&gt;
&lt;li&gt;&lt;i&gt;&lt;b&gt;Comma(,)&lt;/b&gt;&lt;/i&gt; 연산자 인경우 void를 리턴합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655909862633&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
using namespace std;

template&amp;lt;typename... Args&amp;gt;
bool all(Args... args) 
{ 
    return (... &amp;amp;&amp;amp; args); 
}

template&amp;lt;typename... Args&amp;gt;
bool any(Args... args)
{
    return (... || args);
}

template&amp;lt;typename... Args&amp;gt;
void comma(Args... args)
{
    return (... , args);
}


int main() {
    auto a = all();
    auto b = any();
    //auto c = comma(); // error void 리턴
    comma();

    cout &amp;lt;&amp;lt; boolalpha;
    cout &amp;lt;&amp;lt; &quot;a : &quot; &amp;lt;&amp;lt; a &amp;lt;&amp;lt; &quot;, b : &quot; &amp;lt;&amp;lt; b &amp;lt;&amp;lt; '\n';    // a : true, b : false
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;fold expression으로 만드는 간단한 헬퍼 함수&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예제는 첫번째 인자 컨테이너에 매칭 되는 가변 인자의 수를 리턴하는 함수입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655986360391&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전달 받은 가변인수 ts만큼 range를 조회해서 카운트를 구합니다.
template&amp;lt;typename R, typename ... Ts&amp;gt;
auto matches(const R&amp;amp; range, Ts ... ts)
{
    return (std::count(std::begin(range), std::end(range), ts) + ...);
}

void main()
{
    vector&amp;lt;int&amp;gt; v{ 1,2,3,4,5 };
    cout &amp;lt;&amp;lt; matches(v, 2, 5) &amp;lt;&amp;lt; '\\n';           // 2 반환
    cout &amp;lt;&amp;lt; matches(v, 100, 200) &amp;lt;&amp;lt; '\\n';       // 0 반환
    cout &amp;lt;&amp;lt; matches(&quot;abcdefg&quot;, 'x', 'y', 'z') &amp;lt;&amp;lt; '\\n';       // 0 반환
    cout &amp;lt;&amp;lt; matches(&quot;abcdefg&quot;, 'a', 'd', 'f') &amp;lt;&amp;lt; '\\n';       // 3 반환

}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 예제는 Set에 가변인자를 insert하고 모두 성공했는지 확인 하는 헬퍼함수입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655986462793&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 전달 받은 가변인수 모두를 set에 삽입하고 모두 성공했는지 반환 받습니다.
template&amp;lt;typename T, typename ... Ts&amp;gt;
bool insert_all(T &amp;amp; set, Ts ... ts){
    // set.insert의 반환 값을 pair&amp;lt;iterator, bool&amp;gt;의 값을 가짐
    return (set.insert(ts).second &amp;amp;&amp;amp; ...);
}

void main()
{
    set&amp;lt;int&amp;gt; my_set{ 1, 2, 3 };
    cout &amp;lt;&amp;lt; insert_all(my_set, 4, 5, 6) &amp;lt;&amp;lt; '\\n';        // true
    cout &amp;lt;&amp;lt; insert_all(my_set, 7, 8, 2) &amp;lt;&amp;lt; '\\n';        // false =&amp;gt; 2 값을 insert 중복으로 false
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>c++/modern c++</category>
      <category>C++</category>
      <category>c++17</category>
      <category>fold expression</category>
      <category>parameter pack</category>
      <category>template</category>
      <category>폴드 표현식</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/103</guid>
      <comments>https://jungwoong.tistory.com/103#entry103comment</comments>
      <pubDate>Wed, 22 Jun 2022 23:09:31 +0900</pubDate>
    </item>
    <item>
      <title>[c++20] 3중 비교 연산자 (&amp;lt;=&amp;gt;)</title>
      <link>https://jungwoong.tistory.com/102</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;c++20에서는 3중 비교 연산자(&amp;lt;=&amp;gt;)가 추가 되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우주선과 비슷하게 생겨서 우주선 연산자라고 불리웁니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자는 주어진 A와 B에 대해서 A &amp;lt; B인지, 또는 A == B 인지, A &amp;gt; B인지 판정합니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3중 비교 연산자의 테스트 환경&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;221&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/citk7Q/btrE5rRUdPM/XIBGfaDSMwTeWdHrzbnWlK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/citk7Q/btrE5rRUdPM/XIBGfaDSMwTeWdHrzbnWlK/img.png&quot; data-alt=&quot;3중 비교 연산자 컴파일러 지원 옵션&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/citk7Q/btrE5rRUdPM/XIBGfaDSMwTeWdHrzbnWlK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcitk7Q%2FbtrE5rRUdPM%2FXIBGfaDSMwTeWdHrzbnWlK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;221&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;221&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;3중 비교 연산자 컴파일러 지원 옵션&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 프로젝트 우 클릭 -&amp;gt; 구성 속성 -&amp;gt; 일반 -&amp;gt; c++ 언어 표준을 ISO C++ 20 표준으로 수정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWu7sT/btrE4CGfM1J/ZumzUOfcPXYCCLE4pYcSVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWu7sT/btrE4CGfM1J/ZumzUOfcPXYCCLE4pYcSVk/img.png&quot; data-alt=&quot;c++ 언어 표준 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWu7sT/btrE4CGfM1J/ZumzUOfcPXYCCLE4pYcSVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWu7sT%2FbtrE4CGfM1J%2FZumzUOfcPXYCCLE4pYcSVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;184&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;c++ 언어 표준 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/compiler-warnings-by-compiler-version?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VisualStudio 버전별 컴파일러 버전&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;c++ 20 이전의 순서 판정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자의 편리함을 소개 하기 위해서 c++20 이전에 상등/대소 관계 연산자를 설명 드립니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++20 이전에는 ==, !=, &amp;gt;, &amp;gt;=, &amp;lt;, &amp;lt;= 연산자 중에 하나라도 정의 했다면 나머지 관계들도 모두 정의 해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(어떠한 형식을 직관적으로 사용하려면 최소한 SemiRegular(준정규) 형식을 충족 해야 합니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1655513770026&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct MyInt {
    int value; 
    explicit MyInt(int val) : value{val} {}
    bool operator == ( const MyInt &amp;amp; rhs) const {
        return value == rhs.value;
    }
    bool operator != ( const MyInt &amp;amp; rhs) const {
        return !(*this == rhs);
    }
    bool operator &amp;lt; ( const MyInt &amp;amp; rhs) const {
        return value &amp;lt; rhs.value;
    }
    
    bool operator &amp;lt;= ( const MyInt &amp;amp; rhs) const {
        return !(rhs &amp;lt; *this)
    }
    bool operator &amp;gt; ( const MyInt &amp;amp; rhs) const {
        return rhs &amp;lt; *this;
    }
    bool operator &amp;gt;= ( const MyInt &amp;amp; rhs) const {
        return !(*this &amp;lt; rhs);
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;c++ 20 부터의 순서 판정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++20 부터는 3중 비교 연산자를 직접 정의 하거나&amp;nbsp; = default 를 사용해서 컴파일러에 의해 생성된 연산자를 사용할 수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자를 사용하면 코드 중복을 크게 줄일 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655514024976&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;compare&amp;gt;

using namespace std;

struct MyInt {
    int value;
    explicit MyInt(int val) : value{ val } {}

    auto operator==(const MyInt&amp;amp; rhs) const {
        return value == rhs.value;
    }
    // 3중 비교 연산자에 대한 명시적 선언
    auto operator&amp;lt;=&amp;gt;(const MyInt&amp;amp; rhs) const {
        return value &amp;lt;=&amp;gt; rhs.value;
    }
};

struct MyIntDefualt {
    int value;
    // 3중 비교 연산자에 대한 암시적 선언
    explicit MyIntDefualt(int val) : value{ val } {}
    auto operator&amp;lt;=&amp;gt;(const MyIntDefualt&amp;amp; rhs) const = default;
};

int main()
{   
    cout &amp;lt;&amp;lt; boolalpha;

    MyInt myInt1(2017);
    MyInt myInt2(2020);

    cout &amp;lt;&amp;lt; (myInt1 &amp;lt; myInt2) &amp;lt;&amp;lt; '\n';  // true
    cout &amp;lt;&amp;lt; (myInt1 &amp;gt; myInt2) &amp;lt;&amp;lt; '\n';  // false
    cout &amp;lt;&amp;lt; (myInt1 == myInt2) &amp;lt;&amp;lt; '\n';  // false

    MyIntDefualt myIntD1(2017);
    MyIntDefualt myIntD2(2020);
    cout &amp;lt;&amp;lt; (myIntD1 &amp;lt; myIntD2) &amp;lt;&amp;lt; '\n';  // true
    cout &amp;lt;&amp;lt; (myIntD1 &amp;gt; myIntD2) &amp;lt;&amp;lt; '\n';  // false
    cout &amp;lt;&amp;lt; (myIntD1 == myIntD2) &amp;lt;&amp;lt; '\n';  // false
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3중 비교 연산자 구현 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자의 구현 방식을 설명 드리기 전에 c++20의 비교 범주(comparison category) 설명을 진행 하겠습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비교 범주(comparison category)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 범주는 3가지 성질의 성립 여부를 체크해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;관계 연산자 성질
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;T는 여섯가지 비교 연산자(==, !=, &amp;lt; ,&amp;lt;=,&amp;gt;,&amp;gt;=)를 모두 지원해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;동치(equivalent) 성질&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;T의 객체 A와 B가 동치일 때는 f(A) 와 f(B)의 결과도 동일 해야 합니다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;f는 전달받은 인수를 읽기 전용으로 사용한다고 가정합니다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉 동치인 값은 구별 할 수 없습니다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비교 가능 성질&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;T의 모든 값은 비교 가능 해야 합니다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;A와 B에 대해서 A &amp;lt; B, A == B, A &amp;gt; B 중에 하나가 참이어야 합니다.&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;예를 들어 부동소수점은 Nan을 가질 수 있는데 이 값은 비교 가능 성질을 충족하지 못합니다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 71px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;비교 범주 성질&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;std::strong_ordering&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;std::weak_ordering&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px;&quot;&gt;std::partial_ordering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;관계 연산자 성질&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;비교 가능 성질&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;동치(equivalent) 성질&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;성립&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3중 비교 연산자 리턴 값&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자는 문맥에 따라서 std::strong_ordering, std::weak_ordering, std::partial_ordering를 리턴합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(예를 들어 객체가 가지고 있는 변수들이 비교 범주를 얼마나 충족하느냐에 따라서 달라짐)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3종류의 객체들의 구현내용을 확인해 보면 다음과 같은 단순한 구조로 되어 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구조체이며 _Compare_t(signed char) 형식의 `_Value`라는 하나의 멤버 변수를 가집니다.&lt;/li&gt;
&lt;li&gt;리터럴 0과 비교하는 operator들이 선언되어 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;자주 사용하는 불변 객체(less, greater, equal, equivalent, &lt;/span&gt;unordered)를 전역 상수로 등록하여 최적화 합니다.&amp;nbsp;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;less : -1&lt;/li&gt;
&lt;li&gt;greater : 1&lt;/li&gt;
&lt;li&gt;equal : 0&lt;/li&gt;
&lt;li&gt;equivalent : 0&lt;/li&gt;
&lt;li&gt;unordered : -128&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_Value에 대한 설정 조건은 첫번째 인자를 기준으로 두번째 인수와의 관계에 따라서 다름과 같이 설정 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작다면 less(-1)&lt;/li&gt;
&lt;li&gt;같으면 equal(0), &lt;span&gt;equivalent&lt;span&gt; (0)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;크다면 greater(1)&lt;/li&gt;
&lt;li&gt;알수 없다면 &lt;span&gt;unordered(-128)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655557905959&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
#include &amp;lt;compare&amp;gt;

using namespace std;

struct MyInt {
    int value;
    // 3중 비교 연산자에 대한 암시적 선언
    explicit MyInt(int val) : value{ val } {}
    auto operator&amp;lt;=&amp;gt;(const MyInt&amp;amp; rhs) const = default;
};


int main()
{   
    MyInt cpp17(2017);
    MyInt cpp20(2020);

    // result_threeway = strong_ordering::less(-1)
    auto result_threeway_le = cpp17 &amp;lt;=&amp;gt; cpp20;
    // result_threeway = strong_ordering::greater(1)
    auto result_threeway_gr = cpp20 &amp;lt;=&amp;gt; cpp17;
    // result_threeway = strong_ordering::equal(0)
    auto result_threeway_eq = cpp20 &amp;lt;=&amp;gt; cpp20;
   
    cout &amp;lt;&amp;lt; boolalpha;
    cout &amp;lt;&amp;lt; &quot;result_threeway_le : &quot; &amp;lt;&amp;lt; (result_threeway_le &amp;lt; 0) &amp;lt;&amp;lt; '\n';  // true
    cout &amp;lt;&amp;lt; &quot;result_threeway_gr : &quot; &amp;lt;&amp;lt; (result_threeway_gr &amp;gt; 0) &amp;lt;&amp;lt; '\n';  // true
    cout &amp;lt;&amp;lt; &quot;result_threeway_eq : &quot; &amp;lt;&amp;lt; (result_threeway_eq == 0) &amp;lt;&amp;lt; '\n';  // true
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;표현식 재작성&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;c++ 20에서는 표현식 재작성이라는 개념이 &lt;/span&gt;&lt;span style=&quot;font-family: AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif;&quot;&gt;적용 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655559566696&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 비교 연산자들이 다음의 형태로 변경 됩니다.
a OP b    ==&amp;gt;    (a &amp;lt;=&amp;gt; b) OP 0&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 71px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style8&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;기존 코드&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 20px;&quot;&gt;변경 코드&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;a &amp;gt; b&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;(a &amp;lt;=&amp;gt; b) &amp;gt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;a &amp;gt;= b&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;(a &amp;lt;=&amp;gt; b) &amp;gt;= 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;a &amp;lt; b&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px;&quot;&gt;(a &amp;lt;=&amp;gt; b) &amp;lt; 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;a &amp;lt;= b&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;(a &amp;lt;=&amp;gt; b) &amp;lt;= 0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위와 같은 구조로 되어 있기 때문에 3중 비교 연산자로 비교 연산자를 처리할 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1655558295606&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 원본 코드
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;compare&amp;gt;

using namespace std;

struct MyInt {
    //생략..
};

int main()
{   
    MyInt cpp17(2017);
    MyInt cpp20(2020);
    
    auto result1 = cpp17 == cpp20;
    auto result2 = cpp17 != cpp20;
    auto result3 = cpp17 &amp;lt; cpp20;
    auto result4 = cpp17 &amp;lt;= cpp20;
    auto result5 = cpp17 &amp;gt; cpp20;
    auto result6 = cpp17 &amp;gt;= cpp20;
    
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cppinsights.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;cppInsights&lt;/a&gt; 라는 곳을 이용하면 컴파일러에 의해 재작성된 구문을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655558201439&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 컴파일러에 의해서 재작성된 구문
#include &amp;lt;iostream&amp;gt;
#include &amp;lt;compare&amp;gt;

using namespace std;

struct MyInt
{
  // 생략..
  inline constexpr std::strong_ordering operator&amp;lt;=&amp;gt;(const MyInt &amp;amp; rhs) const noexcept = default;
  // ==는 따로 구현됩니다. 
  inline constexpr bool operator==(const MyInt &amp;amp; rhs) const noexcept = default;
};


int main()
{
  MyInt cpp17 = MyInt(2017);
  MyInt cpp20 = MyInt(2020);
  
  // cpp17 == cpp20
  bool result1 = cpp17.operator==(cpp20);
  // !(cpp17 == cpp20)
  bool result2 = !cpp17.operator==(cpp20);
  // (cpp17 &amp;lt;=&amp;gt; cpp20) &amp;lt; 0
  bool result3 = operator&amp;lt;(cpp17.operator&amp;lt;=&amp;gt;(cpp20), __cmp_cat::__unspec(0));
  // (cpp17 &amp;lt;=&amp;gt; cpp20) &amp;lt;= 0
  bool result4 = operator&amp;lt;=(cpp17.operator&amp;lt;=&amp;gt;(cpp20), __cmp_cat::__unspec(0));
  // (cpp17 &amp;lt;=&amp;gt; cpp20) &amp;gt; 0
  bool result5 = operator&amp;gt;(cpp17.operator&amp;lt;=&amp;gt;(cpp20), __cmp_cat::__unspec(0));
  // (cpp17 &amp;lt;=&amp;gt; cpp20) &amp;gt;= 0
  bool result6 = operator&amp;gt;=(cpp17.operator&amp;lt;=&amp;gt;(cpp20), __cmp_cat::__unspec(0));
  return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴파일러 생성하는 3중 비교 연산자&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명드린 c++ Insights 라는 사이트를 이용하면 컴파일러에 의해 생성되는 3중 비교 연산자를 확인 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;operator== 의 정의도 추가되는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655514197400&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// c++Insights 에서 생성되는 암시적 3중 연산자
#include &amp;lt;compare&amp;gt;
struct MyInt
{
  int value;
  inline explicit MyInt(int val)
  : value{val}
  {
  }
  
  inline constexpr std::strong_ordering operator&amp;lt;=&amp;gt;(const MyInt &amp;amp; rhs) const /* noexcept */ = default;
  inline constexpr bool operator==(const MyInt &amp;amp; rhs) const /* noexcept */ = default;
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컴파일 시점 비교&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러에 의해서 생성되는 3중 비교 연산자는 constexpr 이므로 문맥에 따라서 컴파일 시점에 사용 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 MyInt D1, D2를 리터럴 값을 통해서 생성하기 때문에 컴파일 시점에 사용 할&amp;nbsp; 수 있는 변수가 되었고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3중 비교 연산자도 컴파일 시점에 수행 될 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655515379889&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct MyInt {
    int value;
    // 생성자를 constexpr로 지정합니다.
    explicit constexpr MyInt(int val) : value{ val } {}
    auto operator&amp;lt;=&amp;gt;(const MyInt&amp;amp; rhs) const = default;
};


int main()
{   
	// 인자를 리터럴로 전달 받았으므로 D1, D2 컴파일 시점에 사용 가능
    constexpr MyInt D1(2017);
    constexpr MyInt D2(2020);

    // operator&amp;lt;=&amp;gt; 수식도 constexpr 이므로 컴파일 시점 사용 가능
    constexpr bool res = (D1 &amp;lt; D2);

    cout &amp;lt;&amp;lt; boolalpha;
    cout &amp;lt;&amp;lt; &quot;D1 &amp;lt; D2 : &quot; &amp;lt;&amp;lt; res &amp;lt;&amp;lt; '\n';

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어휘순 비교&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러가 생성한 3중 비교 연산자는 어휘순 비교를 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어휘순 비교란 모든 기반 클래스의 모든 비정적(non-static) 멤버들을 선언 순서대로 비교 하는 것을 말합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제서는 Lexico를 어휘순으로 비교 할 때는 a -&amp;gt; b -&amp;gt; c -&amp;gt; d -&amp;gt; e 순서대로 비교를 진행합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655554459501&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;struct Basics {
    int a;
    float b;
    double c;
    char d;
    auto operator&amp;lt;=&amp;gt;(const Basics &amp;amp;) const = default;
};

struct Lexico : public Basics {
    int e;
    auto operator&amp;lt;=&amp;gt;(const Lexico&amp;amp;) const = default;

};


int main()
{   
    constexpr Lexico l1 = { 1, 2.0f, 3.0f, 4, 5 };
    constexpr Lexico l2 = { 1, 2.0f, 3.0f, 4, 5 };

    cout &amp;lt;&amp;lt; boolalpha;
    cout &amp;lt;&amp;lt; &quot;l1 == l2 : &quot; &amp;lt;&amp;lt; (l1 == l2) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l1 != l2 : &quot; &amp;lt;&amp;lt; (l1 != l2) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l1 &amp;lt; l2 : &quot; &amp;lt;&amp;lt; (l1 &amp;lt; l2) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l1 &amp;lt;= l2 : &quot; &amp;lt;&amp;lt; (l1 &amp;lt;= l2) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l1 &amp;gt; l2 : &quot; &amp;lt;&amp;lt; (l1 &amp;gt; l2) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l1 &amp;gt;= l2 : &quot; &amp;lt;&amp;lt; (l1 &amp;gt;= l2) &amp;lt;&amp;lt; '\n';

    /* 
    [결 과]
    l1 == l2 : true
    l1 != l2 : false
    l1 &amp;lt; l2 : false
    l1 &amp;lt;= l2 : true
    l1 &amp;gt; l2 : false
    l1 &amp;gt;= l2 : true
    */

    // 어휘 순으로 비교하다가 e에서 l4가 크다는 판정 확인
    constexpr Lexico l3 = { 1, 2.0f, 3.0f, 4, 5 };
    constexpr Lexico l4 = { 1, 2.0f, 3.0f, 4, 6 };
    cout &amp;lt;&amp;lt; boolalpha;
    cout &amp;lt;&amp;lt; &quot;l3 == l4 : &quot; &amp;lt;&amp;lt; (l3 == l4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l3 != l4 : &quot; &amp;lt;&amp;lt; (l3 != l4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l3 &amp;lt; l4 : &quot; &amp;lt;&amp;lt; (l3 &amp;lt; l4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l3 &amp;lt;= l4 : &quot; &amp;lt;&amp;lt; (l3 &amp;lt;= l4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l3 &amp;gt; l4 : &quot; &amp;lt;&amp;lt; (l3 &amp;gt; l4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;l3 &amp;gt;= l4 : &quot; &amp;lt;&amp;lt; (l3 &amp;gt;= l4) &amp;lt;&amp;lt; '\n';

    /*
    [결 과]
    l3 == l4 : false
    l3 != l4 : true
    l3 &amp;lt; l4 : true
    l3 &amp;lt;= l4 : true
    l3 &amp;gt; l4 : false
    l3 &amp;gt;= l4 : false
    */

    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;최적화된 == 연산자와 != 연산자&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 연산을 할 때 문자열이나 벡터 같은 형식들에 대해서는 최적화할 여지가 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 비교전에 객체의 길이를 먼저 비교 한 후에 다르다면 false를 리턴하는 최적화를 진행 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 컴파일러가 생성하는 3중 비교 연산자는 어휘순 비교를 통해서 처음부터 끝까지 비교 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 수정 사항을 P1185R2라는 수정안이 발표 했고 msvc에서는 2019 16.2 버전에 적용 되었음을 확인 할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;46&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WdaJe/btrE5yKc7QM/1dYQjbGJXYQi1vjzBIkgh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WdaJe/btrE5yKc7QM/1dYQjbGJXYQi1vjzBIkgh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WdaJe/btrE5yKc7QM/1dYQjbGJXYQi1vjzBIkgh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWdaJe%2FbtrE5yKc7QM%2F1dYQjbGJXYQi1vjzBIkgh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;892&quot; height=&quot;46&quot; data-origin-width=&quot;892&quot; data-origin-height=&quot;46&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1185r2.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1185r2.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 수정합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655555078660&quot; class=&quot;arduino&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
bool operator==(vector&amp;lt;T&amp;gt; const&amp;amp; lhs, vector&amp;lt;T&amp;gt; const&amp;amp; rhs)
{
    // short-circuit on size early
    const size_t size = lhs.size();
    if (size != rhs.size()) {
        return false;
    }

    for (size_t i = 0; i != size; ++i) {
        // use ==, not &amp;lt;=&amp;gt;, in all nested comparisons
        if (lhs[i] != rhs[i]) {
            return false;
        }
    }

    return true;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/default_comparisons&quot;&gt;https://en.cppreference.com/w/cpp/language/default_comparisons&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1655556620542&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Default comparisons (since C++20) - cppreference.com&quot; data-og-description=&quot;Provides a way to request the compiler to generate consistent comparison operators for a class. [edit] Syntax return-type class-name::operatorop( const class-name &amp;amp; ) const &amp;amp;(optional) = default; (1) friend return-type operatorop( const class-name &amp;amp;, const&quot; data-og-host=&quot;en.cppreference.com&quot; data-og-source-url=&quot;https://en.cppreference.com/w/cpp/language/default_comparisons&quot; data-og-url=&quot;https://en.cppreference.com/w/cpp/language/default_comparisons&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/language/default_comparisons&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://en.cppreference.com/w/cpp/language/default_comparisons&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Default comparisons (since C++20) - cppreference.com&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Provides a way to request the compiler to generate consistent comparison operators for a class. [edit] Syntax return-type class-name::operatorop( const class-name &amp;amp; ) const &amp;amp;(optional) = default; (1) friend return-type operatorop( const class-name &amp;amp;, const&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;en.cppreference.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>c++/modern c++</category>
      <category>3중 비교</category>
      <category>3중 비교 연산자</category>
      <category>C++</category>
      <category>c++20</category>
      <category>Operator</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/102</guid>
      <comments>https://jungwoong.tistory.com/102#entry102comment</comments>
      <pubDate>Sat, 18 Jun 2022 09:20:25 +0900</pubDate>
    </item>
    <item>
      <title>[c++20] consteval과 constinit</title>
      <link>https://jungwoong.tistory.com/101</link>
      <description>&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;c++ 20에서는 consteval과 constinit라는 두가지 키워드가 추가되었습니다. &lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;consteval과 constinit의 테스트 환경&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;300&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c13ch3/btrEZDErUbZ/HYnhTtCNCwyENgTJskUZjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c13ch3/btrEZDErUbZ/HYnhTtCNCwyENgTJskUZjK/img.png&quot; data-alt=&quot;컴파일러 지원 버전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c13ch3/btrEZDErUbZ/HYnhTtCNCwyENgTJskUZjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc13ch3%2FbtrEZDErUbZ%2FHYnhTtCNCwyENgTJskUZjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;784&quot; height=&quot;300&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;300&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;컴파일러 지원 버전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/compiler-warnings-by-compiler-version?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VisualStudio 버전별 컴파일러 버전&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 프로젝트 우 클릭 -&amp;gt; 구성 속성 -&amp;gt; 일반 -&amp;gt; c++ 언어 표준을 ISO C++ 20 표준으로 수정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dCNiXV/btrEZEwCSuD/qv2Q7WhA8urjzNdL5VbK8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dCNiXV/btrEZEwCSuD/qv2Q7WhA8urjzNdL5VbK8K/img.png&quot; data-alt=&quot;C++ 언어 표준 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dCNiXV/btrEZEwCSuD/qv2Q7WhA8urjzNdL5VbK8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdCNiXV%2FbtrEZEwCSuD%2Fqv2Q7WhA8urjzNdL5VbK8K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;184&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;C++ 언어 표준 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;consteval&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;consteval은 immediate function(즉시 함수)를 생성합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;immediate function란 &lt;/span&gt;&lt;span style=&quot;color: #000000; letter-spacing: 0px;&quot;&gt;하나의 컴파일 상수로 평가되는 함수로 컴파일 시점에 실행됩니다&lt;/span&gt;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉시 함수는 암묵적으로 인라인 함수가 되며 다음의 조건을 충족 해야 합니다.&lt;/span&gt;&lt;/div&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소멸자나 메모리를 할당, 재할당하는 함수 적용 불가&lt;/li&gt;
&lt;li&gt;constexpr 함수의 요구 조건&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제를 보시면 지역 변수 const int a와 리터럴 5의 경우는 컴파일 시점에 인식 할 수 있는 값이라서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 시점에 consteval함수인 sqr의 상수식을 만드는데 문제가 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역 변수 int b의 경우 컴파일 시점에 값을 알 수 없기 때문에 b를 통해서 sqr 상수식을 만드려고 할 때&amp;nbsp; 컴파일 에러가 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655386077995&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

// consteval 함수 
// 하나의 컴파일 상수로 평가됩니다.
consteval int sqr(int n) {
    return n * n;
}


int main()
{
    cout &amp;lt;&amp;lt; &quot;sqr(5) : &quot; &amp;lt;&amp;lt; sqr(5) &amp;lt;&amp;lt; '\n';

    const int a = 5;
    cout &amp;lt;&amp;lt; &quot;sqr(a) : &quot; &amp;lt;&amp;lt; sqr(a) &amp;lt;&amp;lt; '\n';

    int b = 5;
    // cout &amp;lt;&amp;lt; &quot;sqr(b) : &quot; &amp;lt;&amp;lt; sqr(b) &amp;lt;&amp;lt; '\n';  // 컴파일 에러 E3133 consteval 함수 &quot;sqr&quot; 호출에서 유효한 상수 식이 생성되지 않았습니다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://cppinsights.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://cppinsights.io/&lt;/a&gt; 사이트에서 컴파일러가 어떻게 consteval을 변환하는지 확인해 볼 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 컴파일러가 생성한 sqr 함수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inline 키워드를 붙이는 것을 확인 할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655386391450&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// consteval 함수 
// 하나의 컴파일 상수로 평가됩니다.
inline consteval int sqr(int n)
{
  return n * n;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;consteval 와 constexpr 함수의 차이점&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++ 11에 추가된 &lt;span&gt;constexpr과의 차이점은 무엇인지 확인 해 보도록 하겠습니다.&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 60px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.2558%; height: 20px;&quot;&gt;함수 타입&lt;/td&gt;
&lt;td style=&quot;width: 76.7442%; height: 20px;&quot;&gt;함수의 성격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.2558%; height: 20px;&quot;&gt;constexpr&lt;/td&gt;
&lt;td style=&quot;width: 76.7442%; height: 20px;&quot;&gt;문맥 또는 최적화에 따라서 컴파일 시점 또는 런타임 시점에도 실행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 23.2558%; height: 20px;&quot;&gt;consteval&lt;/td&gt;
&lt;td style=&quot;width: 76.7442%; height: 20px;&quot;&gt;반드시 컴파일 시점에만 실행 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 내용을 명확하게 인지하고 상황에 따라서 키워드를 선택 하여 사용 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655387193787&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

// 런타임에 평가 됩니다.
const int sqrRuntime(int n) {
    return n * n;
}

// 하나의 컴파일 상수로 평가됩니다.
consteval int sqrCompileTime(int n) {
    return n * n;
}

// 입력 값에 따라서 컴파일 또는 런타임에 평가 됩니다.
constexpr int sqrRunOrCompileTime(int n) {
    return n * n;
}


int main()
{
    // constexpr 변수는 컴파일 타임에 초기화 되어야 합니다.
    // constexpr auto val1 = sqrRuntime(10);  // 에러 E0028 : 식에 상수 값이 있어야 합니다.
    constexpr auto val2 = sqrCompileTime(10);  // 컴파일 시점
    constexpr auto val3 = sqrRunOrCompileTime(10);  // 컴파일 시점

    int x = 100;

    auto val4 = sqrRuntime(x);  // 런타임 시점
    // auto val5 = sqrCompileTime(x);  // 에러 E3133 : consteval 함수 xxx 호출에서 유횽한 상수 식이 생성되지 않앗습니다.
    auto val6 = sqrRunOrCompileTime(x);  // 컴파일 또는 런타임 시점
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constexpr에 대해서 더 자세히 확인 하시려면 제가 이전에 쓴 글을 참조 부탁드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;constexpr 키워드 글&quot; href=&quot;https://jungwoong.tistory.com/54?category=1102341&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jungwoong.tistory.com/54?category=1102341&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655385631764&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[c++] constexpr 키워드&quot; data-og-description=&quot;일반적인 설명 &amp;nbsp;constexpr 키워드는 c++11에서 소개되고 c++14에서 개선되었습니다. (visual studio 2015부터 지원합니다.) &amp;nbsp;constexpr은 상수식 이며 const처럼 변수에 적용 할 수 있으며 해당 변수에 대한 변.&quot; data-og-host=&quot;jungwoong.tistory.com&quot; data-og-source-url=&quot;https://jungwoong.tistory.com/54?category=1102341&quot; data-og-url=&quot;https://jungwoong.tistory.com/54&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/5TeOP/hyONNo6m9b/42PKQObLyOFmpvouLZ1Mnk/img.png?width=800&amp;amp;height=887&amp;amp;face=0_0_800_887,https://scrap.kakaocdn.net/dn/ixBga/hyOMKgswBJ/LuqSgbpgk8JEgfxaRXGw11/img.png?width=800&amp;amp;height=887&amp;amp;face=0_0_800_887,https://scrap.kakaocdn.net/dn/Qc2k8/hyONUPiOsT/UjcIzEZSyKusSx0jeW3Uvk/img.png?width=808&amp;amp;height=896&amp;amp;face=0_0_808_896&quot;&gt;&lt;a href=&quot;https://jungwoong.tistory.com/54?category=1102341&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jungwoong.tistory.com/54?category=1102341&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/5TeOP/hyONNo6m9b/42PKQObLyOFmpvouLZ1Mnk/img.png?width=800&amp;amp;height=887&amp;amp;face=0_0_800_887,https://scrap.kakaocdn.net/dn/ixBga/hyOMKgswBJ/LuqSgbpgk8JEgfxaRXGw11/img.png?width=800&amp;amp;height=887&amp;amp;face=0_0_800_887,https://scrap.kakaocdn.net/dn/Qc2k8/hyONUPiOsT/UjcIzEZSyKusSx0jeW3Uvk/img.png?width=808&amp;amp;height=896&amp;amp;face=0_0_808_896');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[c++] constexpr 키워드&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 설명 &amp;nbsp;constexpr 키워드는 c++11에서 소개되고 c++14에서 개선되었습니다. (visual studio 2015부터 지원합니다.) &amp;nbsp;constexpr은 상수식 이며 const처럼 변수에 적용 할 수 있으며 해당 변수에 대한 변.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;jungwoong.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;constinit&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constinit 키워드는 저장 기간(storage duration)이 정적이거나 스레드인 변수에 적용 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의 점은 지역 변수에 적용 할 수 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constinit를 변수에 적용하면 컴파일 시점에 초기화 됩니다. 주의 할 점은 상수(const) 형식으로 지정하지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655387973939&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const char* g() { return &quot;dynamic initialization&quot;; }
constexpr const char* f(bool p) { return p ? &quot;constant initializer&quot; : g(); }

constinit const char* c = f(true);  // OK
// constinit const char* d = f(false);  // error  E0028 : 식에 상수 값이 있어야 합니다.&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;변수 초기화&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 초기화시 const, constexpr, constinit의 차이점에 대해서 예제로 알아 봅니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 71px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 18.0232%; height: 20px;&quot;&gt;변수의 타입&lt;/td&gt;
&lt;td style=&quot;width: 81.9768%; height: 20px;&quot;&gt;변수 초기화 성격&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.0232%; height: 17px;&quot;&gt;const&lt;/td&gt;
&lt;td style=&quot;width: 81.9768%; height: 17px;&quot;&gt;상수, 런타임 시점까지 초기화 지연 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.0232%; height: 17px;&quot;&gt;constexpr&lt;/td&gt;
&lt;td style=&quot;width: 81.9768%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;상수, &lt;/span&gt;&lt;span&gt;컴파일 시점 초기화&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 18.0232%; height: 17px;&quot;&gt;constinit&lt;/td&gt;
&lt;td style=&quot;width: 81.9768%; height: 17px;&quot;&gt;비상수, 컴파일 시점 초기화, 지역 변수 선언 불가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1655388553458&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;

using namespace std;

// 상수
constexpr int constexprVal = 1000;
// 비상수
constinit int constinitVal = 1000;


int main()
{    
	const auto constVal = 1000;
	cout &amp;lt;&amp;lt; &quot;constVal: &quot; &amp;lt;&amp;lt; constVal &amp;lt;&amp;lt; '\n';

	//cout &amp;lt;&amp;lt; &quot;++constVal: &quot; &amp;lt;&amp;lt; ++constVal &amp;lt;&amp;lt; '\n';  // 에러
	//cout &amp;lt;&amp;lt; &quot;++constexprVal: &quot; &amp;lt;&amp;lt; ++constexprVal &amp;lt;&amp;lt; '\n';  // 에러
	cout &amp;lt;&amp;lt; &quot;++constinitVal: &quot; &amp;lt;&amp;lt; ++constinitVal &amp;lt;&amp;lt; '\n';  // OK

	constexpr auto localConstexpr = 1000; //  OK
	// constinit auto localConstexpr = 1000; //  에러 : 지멱 변수가 될 수 없습니다.

}&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Static Initialization Order Fiasco(정적 변수 초기화 실패)&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Static Initialization Order Fiasco란 &lt;span style=&quot;letter-spacing: 0px;&quot;&gt;코드의 서로 다른 번역 단위(다른 cpp 파일)에 있는 정적 저장기간의 변수들이 순서에 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;의존적인 문제를 말합니다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;이것에 대해서 설명드리기 전에 정적 변수의 초기화 방식에 대해서 먼저 설명을 드린 후에 진행하도록 하겠습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;951&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YmsKO/btrEZdlzFxD/qntO0WLV0kgMpvxTw5gd70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YmsKO/btrEZdlzFxD/qntO0WLV0kgMpvxTw5gd70/img.png&quot; data-alt=&quot;정적 변수 초기화 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YmsKO/btrEZdlzFxD/qntO0WLV0kgMpvxTw5gd70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYmsKO%2FbtrEZdlzFxD%2FqntO0WLV0kgMpvxTw5gd70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;655&quot; height=&quot;951&quot; data-origin-width=&quot;655&quot; data-origin-height=&quot;951&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;정적 변수 초기화 방식&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;정적 단계(컴파일 시점)에서 상수 표현식을 초기화 할 수 있다면 정적 변수는 해당 상수 값으로 초기화 하고 &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;만약 초기화 할 수 없다면 0으로 셋팅합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;동적 단계(런타임 시점)에서 초기화 되지 않은 정적 변수를 실행 시점의 값으로 초기화 합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;아래의 예제 소스를 보시면 staticA와 staticB가 서로 다른 번역 단위에서 있으면서 의존 관계로 묶여 있습니다,&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;staticB의 값을 초기화 하려면 &lt;span style=&quot;color: #000000;&quot;&gt;staticA 값이 필요합니다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;staticA값은 컴파일 단계에서 초기화 할 수 없기 때문에 기본 값인 0으로 셋팅 후에 런타임에서 초기화 됩니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이런 경우 번역 단위의 초기화 순서에 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;따라서 &lt;span style=&quot;color: #000000;&quot;&gt;staticB의 값이 달라집니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;pre id=&quot;code_1655389074282&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SIOF1.cpp
int sum(int l, int r){
    return l + r;
}

auto staticA = sum(1, 2);

// main.cpp
#include &amp;lt;iostream&amp;gt;
using namespace std;

extern int staticA;
auto staticB = staticA;

int main() {
 
    cout &amp;lt;&amp;lt; &quot;staticB : &quot; &amp;lt;&amp;lt; staticB &amp;lt;&amp;lt; &quot;\n&quot;;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제점을 명확하게 보여주기 위해서는 cl 컴파일러를 통해서 빌드를 진행하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655389459385&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# cpp 파일들의 obj 생성 작업
cl.exe /c main.cpp SIOF1.cpp

# SIOF1.obj 먼저 초기화되는 링크 순서
cl.exe /Fe:test1.exe SIOF1.obj main.obj # test1.exe 파일 생성
test1.exe # staticB : 3 출력

# main.obj 먼저 초기화되는 링크 순서
cl.exe /Fe:test2.exe main.obj SIOF1.obj # test2.exe 파일 생성
test2.exe # staticB : 0 출력&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;c++20 이전의 해결 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++20 이전에는 이 문제를 해결하기 위해서 지역 범위 정적 변수의 지연 초기화를 사용 하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지역 범위에 있는 정적 변수는 처음 사용 될 때 생성된 다는 규칙을 통해서 순서의 의존성을 해결 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655389594549&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SIOF1.cpp
int sum(int l, int r){
    return l + r;
}

int &amp;amp; staticA() {
    // 지역 범위 정적 변수는 처음 사용 될 때 생성 됩니다. 
    static auto staticA = sum(1, 2);
    return staticA;
}

// main.cpp
#include &amp;lt;iostream&amp;gt;
using namespace std;

int &amp;amp; staticA();
// 링크 순서와 상관 없이 이곳에서 처음 사용 되기에 staticB은 일정한 값을 가집니다. 
auto staticB = staticA();

// 생략&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;c++20에서 해결 방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++20에서는 constinit 키워드가 추가되었기 때문에 쉽고 깔끔하게 해결 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constint를 사용하면 반드시 컴파일 시점에 초기화 되는 것을 보장 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655389661988&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// SIOF1.cpp
// constexpr는 컴파일 시점에 평가 할 수 있다면 컴파일 시점 평가 함수로 평가 됩니다.
constexpr int sum(int l, int r){
    return l + r;
}

// staticA는 컴파일 시점에 초기화 됩니다.
constinit auto staticA = sum(1, 2);

// main.cpp
#include &amp;lt;iostream&amp;gt;
using namespace std;

extern constinit int staticA;
auto staticB = staticA;

// 생략&lt;/code&gt;&lt;/pre&gt;</description>
      <category>c++/modern c++</category>
      <category>C++</category>
      <category>c++20</category>
      <category>const</category>
      <category>consteval</category>
      <category>constexpr</category>
      <category>constinit</category>
      <category>immediate function</category>
      <category>Static Initialization Order Fiasco</category>
      <category>정적변수 초기화</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/101</guid>
      <comments>https://jungwoong.tistory.com/101#entry101comment</comments>
      <pubDate>Thu, 16 Jun 2022 22:39:10 +0900</pubDate>
    </item>
    <item>
      <title>[c++20] module(모듈)</title>
      <link>https://jungwoong.tistory.com/100</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Module의 테스트 환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concept를 지원하는 컴파일러 버전을 확인하시려면 다음의 경로에서 확인 할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/compiler_support/20&quot;&gt;https://en.cppreference.com/w/cpp/compiler_support/20&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;모듈화 지원버전.png&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;285&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzEeVc/btrEI9p9H5M/jkkDnMC9KmGpRtQz8iOGek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzEeVc/btrEI9p9H5M/jkkDnMC9KmGpRtQz8iOGek/img.png&quot; data-alt=&quot;module 컴파일러 지원 버전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzEeVc/btrEI9p9H5M/jkkDnMC9KmGpRtQz8iOGek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzEeVc%2FbtrEI9p9H5M%2FjkkDnMC9KmGpRtQz8iOGek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;285&quot; data-filename=&quot;모듈화 지원버전.png&quot; data-origin-width=&quot;797&quot; data-origin-height=&quot;285&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;module 컴파일러 지원 버전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;저의 경우 MSVC 2019 community&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;16.11.15 버전이고 컴파일러 버전은 19.29을 사용하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMwvtF/btrEHinS0Lh/yqLSfKAM0pj9IDk8SunNQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMwvtF/btrEHinS0Lh/yqLSfKAM0pj9IDk8SunNQK/img.png&quot; data-alt=&quot;사용한 컴파일러 버전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMwvtF/btrEHinS0Lh/yqLSfKAM0pj9IDk8SunNQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMwvtF%2FbtrEHinS0Lh%2FyqLSfKAM0pj9IDk8SunNQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;473&quot; height=&quot;42&quot; data-origin-width=&quot;473&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사용한 컴파일러 버전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 프로젝트 우 클릭 -&amp;gt; 구성 속성 -&amp;gt; 일반 -&amp;gt; c++ 언어 표준을 ISO C++ 20 표준으로 수정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d29f3n/btrEInWhq5H/Tp8nUhPUlh0muJfDBo64LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d29f3n/btrEInWhq5H/Tp8nUhPUlh0muJfDBo64LK/img.png&quot; data-alt=&quot;C++ 컴파일러 버전 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d29f3n/btrEInWhq5H/Tp8nUhPUlh0muJfDBo64LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd29f3n%2FbtrEInWhq5H%2FTp8nUhPUlh0muJfDBo64LK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;184&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;C++ 컴파일러 버전 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈을 설명하기 전에 모듈이 도입되어야 했던 이유를 설명하기 위해서 기존의 c++ 빌드 과정과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제점에 대해서 먼저 알아보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;기존의 C++ 빌드 과정&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;빌드과정.png&quot; data-origin-width=&quot;1613&quot; data-origin-height=&quot;815&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/egRX2B/btrEE1gf8ll/KStMifWRH5uyAoJ0bYJy60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/egRX2B/btrEE1gf8ll/KStMifWRH5uyAoJ0bYJy60/img.png&quot; data-alt=&quot;c++빌드 과정(https://st-lab.tistory.com/176)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/egRX2B/btrEE1gf8ll/KStMifWRH5uyAoJ0bYJy60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FegRX2B%2FbtrEE1gf8ll%2FKStMifWRH5uyAoJ0bYJy60%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1613&quot; height=&quot;815&quot; data-filename=&quot;빌드과정.png&quot; data-origin-width=&quot;1613&quot; data-origin-height=&quot;815&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;c++빌드 과정(https://st-lab.tistory.com/176)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C++ 프로그램의 빌드 과정은 전처리, 컴파일, 링크로 구성됩니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;전처리(Preprocessor)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리기는 소스 파일에 있는 #include나 #define 같은 지시문들을 처리합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#include 지시문을 헤더 파일로 치환하고 #define 지시문도 정의된 매크로로 치환합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#if, #else, #elif, #ifdef, #ifndef , #endif 같은 지시문을 기반으로 조건부 처리로 포함하거나 제외합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전처리기에서 생성된 구문을 번역 단위(translation unit) 라고 불리우고 이것을 컴파일러에 전달 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴파일(compilation)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일 과정에서는 전달 받은 번역 단위에 담긴 c++ 소스 코드를 해석해서 어셈블리 코드로 변환합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어셈블리 코드에 대응 되는 이진 파일을 출력하는데 이것을 목적(object) 파일이라고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적 파일들은 목적 파일이 정의하지 않은 Symbol들도 참조 할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;링크(linking)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적(Object) 파일들을 링크해서 하나의 실행 파일이나 공유, 정적 라이브러리 파일을 만듭니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 목적 파일들의 Symbol을 검사해서 정상적인 참조인지 확인합니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;빌드 과정의 문제점&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;치환 문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 선언된 간단한 프로그램이 있다고 생각해봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655123546302&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// main.cpp
int main()
{
    return 0;
}

// main1.cpp
#include &amp;lt;iostream&amp;gt;
int main()
{
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cl이나 g++ 명령어에 옵션을 통해서 빌드과정에서 생성되는 번역단위를 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655123546304&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# msvc의 경우
cl.exe /std:c++20 /c main.cpp /E &amp;gt; trans.log
cl.exe /std:c++20 /c main1.cpp /E &amp;gt; trans1.log

# gcc의 경우
g++ -E main.cpp &amp;gt; trans.log
g++ -E main1.cpp &amp;gt; trans1.log&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pd5oe/btrEHDlsemq/7sWKVsgDEkh11Fbwdwsif1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pd5oe/btrEHDlsemq/7sWKVsgDEkh11Fbwdwsif1/img.png&quot; data-alt=&quot;생성된 번역단위 크기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pd5oe/btrEHDlsemq/7sWKVsgDEkh11Fbwdwsif1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPd5oe%2FbtrEHDlsemq%2F7sWKVsgDEkh11Fbwdwsif1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;50&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;생성된 번역단위 크기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 프로그램을 동작하는데 trans1.log 크기가 큰 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;#include &amp;lt;iostream&amp;gt;에 의한 치환 과정에서 iostream에서 참조하는 헤더들과 또 그 헤더들이 참조하는 헤더들이&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩적인 과정을 통해서 번역 단위가 커지는 것을 확인 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전처리 매크로의 위험&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매크로는 단순한 텍스트 치환으로 동작하는데 c++의 의미론과 무관하게 동작합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655123694603&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// common.h
#define SIZE	10

// item.h
#define SIZE	100&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 같은 전처리 매크로 값에 충돌이 발생하고 cpp 파일에서 두 헤더를 참조한다고 할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순서를 어떻게 하느냐에 따라서 &quot;SIZE&quot; 값이 달라집니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기호 중복 정의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++에는 ODR이라는 용어가 있는데 One Definition Rule 입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;번역 단위나 프로그램에서 함수의 선언은 하나이어야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++에서는 해당 중복 정의를 회피하기 위해서 포함 가드(include guard)를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655123952228&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// header.h
// #pragma once
#ifndef FUNC_H
#define FUNC_H

void func1() {

#endif&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모듈의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모듈은 단 한번만 도입되며 비용이 거의 없습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;모듈을 도입하는 순서에 따른 차이가 없습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;모듈에서는 기호 중복 정의 문제가 거의 발생하지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;모듈은 코드의 논리적 구조를 표현하는데 유리합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명시적으로 노출할 대상을 지정할 수 있습니다.&lt;/li&gt;
&lt;li&gt;다수의 모듈을 모아서 하나의 논리적 패키지로 제공할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;소스 코드를 인터페이스 부분과 구현 부분으로 분리할 필요가 없습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모듈의 구조&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;컴파일러 별 모듈 파일&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴파일러 별로 모듈에 대한 확장자를 다르게 설정 하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MSVC의 경우 ixx 확장자를 모듈로 사용합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Clang의 경우 cppm 확장자를 사용 했으나 최근에는 cpp를 사용합니다.&lt;/li&gt;
&lt;li&gt;GCC의 경우 모듈 파일에 대해 특별한 확장자를 사용하지 않습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모듈의 구조&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;module로 시작하는 구문과 모듈의 선언 사이에는 &lt;b&gt;전역 모듈 조각&lt;/b&gt;이라는 공간이 존재합니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전역 모듈 조각에는 아직 모듈화 되지 않은 헤더 파일을 추가 할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;'&lt;b&gt;export module xxx&lt;/b&gt;'은 모듈의 선언입니다. 여기서 부터 모듈이 시작됩니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라이언트는 선언된 모듈 이름을 통해서 모듈을 사용 할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;모듈안에서 참조할 모듈을 &lt;b&gt;import&lt;/b&gt;를 통해서 가져올 수 있습니다.&lt;/li&gt;
&lt;li&gt;내보낼 모듈들은 export namespace로 선언된 공간에 선언합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;내보내지 않을 선언들은 export를 포함하지 않고 선언합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655203879612&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module; // 전역 모듈 조각

#include &amp;lt;string&amp;gt;		// 아직 모듈화 되지 않는 라이브러리 헤더

export module test;     // 모듈 선언, 여기서 부터 모듈 시작
import test2            // 사용할 모듈

// 내부에서만 사용할 선언들
const char* _getName() { return &quot;test&quot;; }

// 외부에 노출할 선언들
export namespace test {
	std::string name() { return std::string{ _getName() }; }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;모듈 인터페이스 단위와 모듈 구현 단위&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈이 커지면 모듈을 하나의 모듈 인터페이스 단위와 하나 이상의 모듈 구현 단위로 분할 하는 것을 권장합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모듈 인터페이스&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 인터페이스에는 모듈 선언을 내보내는 선언이 있어야 합니다. (&quot;export module math&quot;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈이 내보낼 함수들을 정의합니다. 하나의 모듈에는 모듈 인터페이스는 하나이어야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655206535190&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;module;

#include &amp;lt;string&amp;gt;

export module math;

export namespace math {
    int add(int fir, int sec);
    int sub(int fir, int sec);

    class Vec {
    public:
        std::string getName();
    };
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;모듈 구현 단위&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈 구현 단위에도 모듈 선언이 있어야 하지만 export는 붙이지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 모듈에 여러개의 모듈 구현 단위가 있을 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655207290558&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// mathImplementationUnit.cpp
module;
#include &amp;lt;numeric&amp;gt;
#include &amp;lt;string&amp;gt;

module math;

namespace math {
    int add(int fir, int sec) {
        return fir + sec;
    }

    int sub(int fir, int sec) {
        return fir - sec;
    }

    std::string Vec::getName() {
        return std::string{ &quot;Vec&quot; };
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cpp에서 모듈 사용 예제&lt;/p&gt;
&lt;pre id=&quot;code_1655209050390&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// main.cpp
import math;
#include &amp;lt;iostream&amp;gt;


int main()
{
    math::Vec vec;
    auto a = vec.getName();
    std::cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; std::endl;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;하위 모듈&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모듈이 더욱 더 커지면 하위 모듈이나 모듈 분할이라는 방식으로 더 작은 단위로 관리 할수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제에서는 math를 math.math1, math.math2라는 하위 모듈로 나눠서 정의합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655210982044&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// mathModule.ixx
export module math;

export module math.math1;
export module math.math2;

// mathModule1.ixx
export module math.math1;
export int add(int fir, int sec) {
    return fir + sec;
}

// mathModule2.ixx
export module math.math1;
export int sub(int fir, int sec) {
        return fir - sec;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자는 다음과 같이 사용 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655211149335&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt;
import math;

int main() {
    cout &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;add(3,4): &quot; &amp;lt;&amp;lt; add(3,4) &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;sub(3,4): &quot; &amp;lt;&amp;lt; sub(3,4) &amp;lt;&amp;lt; '\n';
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또는 직접 하위 모듈에 접근해서 사용 할 수도 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655211176040&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// mathModuleClient1.cpp
// 하위 모듈을 직접 접근 할 수 있습니다. 
#include &amp;lt;iostream&amp;gt;
import math.math1;

int main() {
    cout &amp;lt;&amp;lt; '\n';
    cout &amp;lt;&amp;lt; &quot;add(3,4): &quot; &amp;lt;&amp;lt; add(3,4) &amp;lt;&amp;lt; '\n';
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;헤더 단위&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더 단위(header unit)란 전통적인 헤더에서 모듈로 넘어가기 위한 방법입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 #include 지시문에서 import 지시자로 바꾸고 세미콜론(;)을 붙이면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655209496179&quot; class=&quot;autoit&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#include &amp;lt;iostream&amp;gt; =&amp;gt; import &amp;lt;iostream&amp;gt;;

#include &quot;MyHeader.h&quot; =&amp;gt; import &quot;MyHeader.h&quot;;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 헤더 단위를 사용하면 컴파일러는 모듈에서 export 한것 처럼 처리하기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 #include 보다 빠릅니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;헤더 단위의 단점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 헤더를 헤더 단위로 도입할 수 없습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++ 표준 라이브러리는 도입 가능하지만 C 헤더들은 도입 불가입니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MSVC에서 STL 라이브러리를 헤더 단위로 사용해보기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;msdn에서는 2가지 방법을 제시합니다ㅏ.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;STL 라이브러리의 헤더 단위 정적 라이브러리 만들기&lt;/li&gt;
&lt;li&gt;STL 헤더를 검색하고 헤더 단위로 컴파일 하는 방법&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 간단한 2번째 방법으로 진행해봅니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;프로젝트 옵션 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 속성 페이지로 이동합니다.&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;구성, 플랫폼을 모든 구성, 모든 플랫폼으로 수정합니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;구성 속성 -&amp;gt; C/C++ -&amp;gt; 일반의 아래 속성을 다음과 같이 변경합니다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwSdId/btrEKVfS6D1/gIcnDisYBCrvU874rHG6cK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwSdId/btrEKVfS6D1/gIcnDisYBCrvU874rHG6cK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwSdId/btrEKVfS6D1/gIcnDisYBCrvU874rHG6cK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwSdId%2FbtrEKVfS6D1%2FgIcnDisYBCrvU874rHG6cK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;549&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;549&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정 후에 다음과 같이 입력후 빌드를 실행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 실행시에 include 지시문으로 import 지시문으로 변환하는데 시간이 오래 걸립니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655210754127&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import math;
import &amp;lt;iostream&amp;gt;;


int main()
{
    std::cout &amp;lt;&amp;lt; &quot;import 빌드 완료&quot; &amp;lt;&amp;lt; std::endl;
    return 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units?view=msvc-160&quot;&gt;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units?view=msvc-160&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1655209286379&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;연습: STL 라이브러리를 헤더 단위로 가져오기&quot; data-og-description=&quot;헤더 단위를 사용하여 Visual Studio에서 C++ STL(표준 템플릿 라이브러리) 라이브러리를 가져오는 방법을 알아봅니다.&quot; data-og-host=&quot;docs.microsoft.com&quot; data-og-source-url=&quot;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units?view=msvc-160&quot; data-og-url=&quot;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/yj1RS/hyOK5krRUQ/mosYjWBxkKfSHRbHuDu0y1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/LL2Aq/hyOLfm29re/XgXcKD4THPsOP0B4IPo1n0/img.png?width=1125&amp;amp;height=618&amp;amp;face=0_0_1125_618,https://scrap.kakaocdn.net/dn/E3YOB/hyOK5Lvyph/pSeCgckYEcpUqjqA4AKFe1/img.png?width=789&amp;amp;height=548&amp;amp;face=0_0_789_548&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units?view=msvc-160&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.microsoft.com/ko-kr/cpp/build/walkthrough-import-stl-header-units?view=msvc-160&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/yj1RS/hyOK5krRUQ/mosYjWBxkKfSHRbHuDu0y1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400,https://scrap.kakaocdn.net/dn/LL2Aq/hyOLfm29re/XgXcKD4THPsOP0B4IPo1n0/img.png?width=1125&amp;amp;height=618&amp;amp;face=0_0_1125_618,https://scrap.kakaocdn.net/dn/E3YOB/hyOK5Lvyph/pSeCgckYEcpUqjqA4AKFe1/img.png?width=789&amp;amp;height=548&amp;amp;face=0_0_789_548');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;연습: STL 라이브러리를 헤더 단위로 가져오기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;헤더 단위를 사용하여 Visual Studio에서 C++ STL(표준 템플릿 라이브러리) 라이브러리를 가져오는 방법을 알아봅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.microsoft.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>c++/modern c++</category>
      <category>C++</category>
      <category>c++20</category>
      <category>cpp</category>
      <category>h</category>
      <category>ixx</category>
      <category>Module</category>
      <category>모듈</category>
      <category>빌드</category>
      <category>헤더 단위</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/100</guid>
      <comments>https://jungwoong.tistory.com/100#entry100comment</comments>
      <pubDate>Mon, 13 Jun 2022 21:26:28 +0900</pubDate>
    </item>
    <item>
      <title>[c++20] Concept</title>
      <link>https://jungwoong.tistory.com/99</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept의 테스트 환경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;concept를 지원하는 컴파일러 버전을 확인하시려면 다음의 경로에서 확인 할 수 있습니다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://en.cppreference.com/w/cpp/compiler_support/20&quot;&gt;https://en.cppreference.com/w/cpp/compiler_support/20&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;547&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nGfvA/btrEuyK6Vs5/RyvCQUa5bC1ZVKwJskAUw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nGfvA/btrEuyK6Vs5/RyvCQUa5bC1ZVKwJskAUw0/img.png&quot; data-alt=&quot;concept 컴파일러 지원 버전&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nGfvA/btrEuyK6Vs5/RyvCQUa5bC1ZVKwJskAUw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnGfvA%2FbtrEuyK6Vs5%2FRyvCQUa5bC1ZVKwJskAUw0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;804&quot; height=&quot;547&quot; data-origin-width=&quot;804&quot; data-origin-height=&quot;547&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;concept 컴파일러 지원 버전&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.microsoft.com/ko-kr/cpp/error-messages/compiler-warnings/compiler-warnings-by-compiler-version?view=msvc-170&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VisualStudio 버전별 컴파일러 버전&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 프로젝트 우 클릭 -&amp;gt; 구성 속성 -&amp;gt; 일반 -&amp;gt; c++ 언어 표준을 ISO C++ 20 표준으로 수정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blJNB8/btrEumEbnps/IvMn3Y4U1tsVJYhz7dwc1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blJNB8/btrEumEbnps/IvMn3Y4U1tsVJYhz7dwc1K/img.png&quot; data-alt=&quot;C++ 컴파일러 버전 변경&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blJNB8/btrEumEbnps/IvMn3Y4U1tsVJYhz7dwc1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblJNB8%2FbtrEumEbnps%2FIvMn3Y4U1tsVJYhz7dwc1K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;184&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;C++ 컴파일러 버전 변경&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept이 나오게 된 배경&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;c++ 20 이전에는 함수나 클래스에 접근 하는 방식은 다음과 같았습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;명시적인(specific) 호출&lt;/li&gt;
&lt;li&gt;템플릿을 사용한 generic 호출&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적인 호출을 사용한 방식을 사용하면 필요한 모든 데이터 형식에 대한 모든 함수를 Overloading 해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 작업은 많은 비용이 소모되기도 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다고 c++의 암묵적인 변환을 사용하게 되면 의도치 않은 문제가 발생합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654870483273&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void printInt(int i) {
    std::cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; '\n';
}

template&amp;lt;typename T&amp;gt;
auto add(T first, T second) {
    return first + second;
}

int main()
{
    printInt(true);         // 암시적 변환 : 1

    printInt(3.15);         // 암시적 변환  : 3

    // bool -&amp;gt; int로 승격
    // ret은 int로 변경
    cout &amp;lt;&amp;lt; add(true, false) &amp;lt;&amp;lt; &quot;\n&quot;;  // 암시적 변환  : 1
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 문제를 해결 하기 위해서 함수 템플릿이나 클래스 템플릿을 사용 할 수 있지만&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명시적인 제약 사항이 존재하지 않아서 이러한 문제로 오류 발생시 문제를 검출하기가 까다로웠습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654871044561&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;std::list&amp;lt;int&amp;gt; myList{ 1, 10, 3, 2, 5 };

// std::sort는 임의 접근 반복자만 허용합니다. 
// 사용자는 임의 접근 반복자만 요구하는 것을 명시적으로 알 수 없습니다.
// 복잡한 오류 메시지가 사용자를 반겨줍니다.
std::sort(begin(myList), end(myList));&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 문제를 해결하기 위해서 c++20에서는 Concept라는 개념이 추가되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Concept는 컴파일 시점의 술어로써 템플릿 매개변수에 의미론적(sementic)인 제약을 가합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서의 std::sort에 std::random_access_iterator라는 concept를 제약 사항으로 설정 한 다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;list의 iterator는 std::bidirectional_iterator의 콘셉트만 지원하고 std::random_access_iterator는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지원하지 않는다는 명확한 오류 메시지를 전달 할 수 있다는 장점을 가집니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept의 장점&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;템플릿 매개변수에 대한 요구 조건들이 인터페이스의 일부가 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;콘셉트를 기반으로 함수를 중복 적재하거나 클래스 템플릿을 특수화 할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;클래스나 클래스 템플릿의 일반적 멤버 함수에도 콘셉트를 적용 할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;컴파일러는 템플릿 매개변수에 대한 요구 조건들을 기반으로 좀 더 개선된 오류 메시지를 생성 할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;다른 사람들과 미리 정의한 콘셉트를 활용 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;auto와 콘셉트 용법이 통합 되었습니다. auto 대신 콘셉트를 사용 할 수 있습니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;c++ 14에서는 일반적 람다가 도입되었습니다.&lt;/li&gt;
&lt;li&gt;c++ 20에서는 제약 있는 자리표(contrained placeholder)와 제약 없는 자리표(uncontrained placeholder)을 사용하면 auto로 선언된 함수가 있으면 자동으로 템플릿 함수로 변환됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;함수 선언에 콘셉트가 있으면 그 함수 선언은 자동으로 함수 템플릿이 됩니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept의 적용 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콘셉트를 적용하는 여러 가지 방법에 대해서 예제로 공유드립니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;requires 절 선언&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requires라는 키워드로 시작하는 requires 절은 템플릿 매개변수나 함수 선언에 대한 요구 조건 또는 제약을 서술 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;requires 키워드 다음에는 다음과 같은 형식이 올 수 있습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;하나의 명명된 콘셉트&lt;/li&gt;
&lt;li&gt;명명된 콘셉트의 논리곱(&amp;amp;&amp;amp;)이나 논리 합(||)&lt;/li&gt;
&lt;li&gt;requires 표현식 같은 컴파일 시점 술어(compile-time predicate)&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1654871614914&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// require 절 행
// 하나의 명명된 콘셉트가 사용됨
template&amp;lt;typename T&amp;gt;
requires std::integral&amp;lt;T&amp;gt;
auto gcd(T a, T b) {
    if (b == 0) return a;
    else return gcd(b, a % b);
}

// 명명된 콘셉트의 논리곱(&amp;amp;&amp;amp;)
template&amp;lt;typename T&amp;gt;
requires std::integral&amp;lt;T&amp;gt; &amp;amp;&amp;amp; std::movable&amp;lt;T&amp;gt;
auto gcd2(T a, T b) {
    if (b == 0) return a;
    else return gcd(b, a % b);
}
// 비형식 인수 컴파일 시점 술어
template&amp;lt;int i&amp;gt;
requires(i &amp;gt;= 0)
int64_t multiplication(int j)
{
    return (i * j);
}

int main()
{
    // std::integral는 템플릿 매개 변수를 기본형 정수로 제한합니다.
    cout &amp;lt;&amp;lt; gcd(10, 100) &amp;lt;&amp;lt; '\n';  // 10
    //cout &amp;lt;&amp;lt; gcd(10.0f, 100.0f) &amp;lt;&amp;lt; '\n';  // error 관련 제약 조건이 충족되지 않습니다.

    cout &amp;lt;&amp;lt; multiplication&amp;lt;2&amp;gt;(3) &amp;lt;&amp;lt; '\n';  // 6;
    //cout &amp;lt;&amp;lt; multiplication&amp;lt;-1&amp;gt;(3) &amp;lt;&amp;lt; '\n';  // error 관련 제약 조건이 충족되지 않습니다.
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;후행 require 절 선언&lt;/h4&gt;
&lt;pre id=&quot;code_1654950732016&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 후행 requires 절 행
// 하나의 명명된 콘셉트가 사용됨
template&amp;lt;typename T&amp;gt;
auto gcd(T a, T b) requires std::integral&amp;lt;T&amp;gt; {
    if (b == 0) return a;
    else return gcd1(b, a % b);
};&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;템플릿 매개변수로 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;템플릿 매개변수에 콘셉트를 지정할 수 도 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654950458616&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 제약이 있는 템플릿 매개변수
template&amp;lt;std::integral T&amp;gt;
auto gcd(T a, T b){
    if (b == 0) return a;
    else return gcd2(b, a % b);
}

// 단축 함수 템플릿 - 제약 있는 형식 매개변수 
// 이 형태만 a와 b의 형식이 다를 수 있습니다.
std::integral auto gcd1(std::integral auto a, std::integral auto b){
    if (b == 0) return a;
    else return gcd3(b, a % b);
}

template&amp;lt;typename T&amp;gt;
struct Test {};

// 템플릿 특수화로 사용된 콘셉트
template&amp;lt;std::regular Reg&amp;gt;
struct Test&amp;lt;Reg&amp;gt; {};

// 가변 인수 템플릿에서 사용
template&amp;lt;std::integral... Args&amp;gt;
bool all(Args... args) { return (... &amp;amp;&amp;amp; args);  }

template&amp;lt;std::integral... Args&amp;gt;
bool any(Args... args) { return (... || args); }

template&amp;lt;std::integral... Args&amp;gt;
bool none(Args... args) { return !(... || args); }

int main()
{
    Test&amp;lt;int&amp;gt; t;        // std:regular를 통한 특수화
    Test&amp;lt;int&amp;amp;&amp;gt; t2;      // 일반 템플릿 Test 구조체 호출   
    
    cout &amp;lt;&amp;lt; all(5, true, false) &amp;lt;&amp;lt; '\n';        // false
    cout &amp;lt;&amp;lt; any(5, true, false) &amp;lt;&amp;lt; '\n';        // true
    cout &amp;lt;&amp;lt; none(5, true, false) &amp;lt;&amp;lt; '\n';      // false
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;함수 중복 적재(overloading)에서 사용&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;함수 중복 적재시에도 콘셉트를 지정 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1654953953514&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void overload(auto t) {
    cout &amp;lt;&amp;lt; &quot;auto : &quot; &amp;lt;&amp;lt; t &amp;lt;&amp;lt; '\n';
}

// 콘셉트를 사용해서 범위를 지정할 수 있습니다. 
void overload(std::integral auto t) {
    cout &amp;lt;&amp;lt; &quot;integral : &quot; &amp;lt;&amp;lt; t &amp;lt;&amp;lt; '\n';
}

void overload(long l) {
    cout &amp;lt;&amp;lt; &quot;long : &quot; &amp;lt;&amp;lt; l &amp;lt;&amp;lt; '\n';
}


int main()
{
    overload(3.14);  // auto : 3.14
    overload(2020);  // integral : 2020
    overload(2022L);  // long : 2022
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Concept의 정의 방법&lt;/h2&gt;
&lt;pre id=&quot;code_1654873841483&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// concept 정의 구문
template &amp;lt;template-parameter-list&amp;gt;
concept concept-name = constraint-expression;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;constraint-expression은 다음 중 하나입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 concept나 컴파일 시점 술어의 논리 조합&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;논리 조합에는 논리 연산자( &amp;amp;&amp;amp;, ||, ! ) 을 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;컴파일 시점의 술어는 컴파일 시점에 bool 값을 돌려주는 호출 가능 객체 입니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1654873828695&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// concepts 파일에 정의된 integral, signed_integral, unsigned_integral의 형식

// integral은 is_integral_v 컴파일 술어를 사용해서 정의합니다.
template &amp;lt;class _Ty&amp;gt;
concept integral = is_integral_v&amp;lt;_Ty&amp;gt;;

// unsigned_integral은 콘셉트인 integral, signed_integral을 사용해서 정의합니다.
template &amp;lt;class _Ty&amp;gt;
concept unsigned_integral = integral&amp;lt;_Ty&amp;gt; &amp;amp;&amp;amp; !signed_integral&amp;lt;_Ty&amp;gt;;

// signed_integral는 콘셉트인 integral을 사용해서 정의합니다. 
// static_cast&amp;lt;_Ty&amp;gt;(-1) &amp;lt; static_cast&amp;lt;_Ty&amp;gt;(0) 컴파일 술어를 사용
template &amp;lt;class _Ty&amp;gt;
concept signed_integral = integral&amp;lt;_Ty&amp;gt; &amp;amp;&amp;amp; static_cast&amp;lt;_Ty&amp;gt;(-1) &amp;lt; static_cast&amp;lt;_Ty&amp;gt;(0);&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;요구조건 표현식&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1654955241350&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 요구 조건 표현식 
// parameter-list : 함수 선언의 파라미터와 같음
// requirement-seq : 단순 요구조건, 형식 요구조건, 복합 요구 조건, 중첩 요구 조건으로 구성된 순차열
requires ( parameter-list(optional) ) { requirement-seq }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;요구 조건 표현식 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;단순 요구 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 예제와 같이 괄호에 묶여서 선언되어 사용됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655034149833&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Addable는 T타입에 대해서 a + b 표현식이 유효해야 합니다.
template&amp;lt;typename T&amp;gt;
concept Addable = requires(T a, T b) {
	a + b;
};

// Subtractable는 T타입에 대해서  a - b 표현식이 유효해야 합니다.
template&amp;lt;typename T&amp;gt;
concept Subtractable = requires(T a, T b) {
	a - b;
};

// Swappable은 T, U가 swap 표현식을 만족 해야 합니다.
template&amp;lt;typename T, typename U = T&amp;gt;
concept Swappable = requires(T&amp;amp;&amp;amp; t, U&amp;amp;&amp;amp; u)
{
    swap(std::forward&amp;lt;T&amp;gt;(t), std::forward&amp;lt;U&amp;gt;(u));
    swap(std::forward&amp;lt;U&amp;gt;(u), std::forward&amp;lt;T&amp;gt;(t));
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;형식 요구 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;형식 요구 조건을 표현 할 때는 &lt;b&gt;typename&lt;/b&gt;과 형식 이름을 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1655034760343&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;typename T&amp;gt;
using Ref = T&amp;amp;;

template&amp;lt;typename T&amp;gt;
concept RequirementType = requires {
    // T가 key_type을 가져야 합니다. 
    typename T::key_type;
    typename T::allocator_type;
    // Ref를 T로 인스턴스화 할 수 있어야 합니다.
    typename Ref&amp;lt;T&amp;gt;;
};

auto printValueType(RequirementType auto a) {
    cout &amp;lt;&amp;lt; typeid(a).name() &amp;lt;&amp;lt; endl;
}

int main()
{
    // printValueType(std::vector&amp;lt;int&amp;gt;{1, 2, 3});  // 관련 제약 조건이 충족되지 않습니다
    printValueType(std::map&amp;lt;int, int&amp;gt;{ {1, 2}, { 2, 3 }, { 3, 4 } });
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;복합 요구 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복합 요구 조건이 단순 요구 조건가 다른 점은&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;퓨현식에 {}(중괄호)를 감싸야 합니다.&lt;/li&gt;
&lt;li&gt;noexcept 지정자와 반환 형식 요구조건을 추가로 붙일 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1655037224129&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 복합 요구 조건은 다음의 형식을 만족해야 합니다.
{ expression } noexcept(optional) return-type-requirement(optional) ;		


template&amp;lt;typename T&amp;gt;
concept C2 = requires(T x)
{
    // *x라는 표현식이 유효해야 합니다
    // T::inner라는 타입도 유효 해야 합니다. 
    // *x의 결과는 T::inner로 변환 가능해야합니다.
    {*x} -&amp;gt; std::convertible_to&amp;lt;typename T::inner&amp;gt;;
 
    // x + 1라는 표현식이 유효 해야 하며 그 결과가 int형식이어야 합니다.
    {x + 1} -&amp;gt; std::same_as&amp;lt;int&amp;gt;;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;중첩 요구 조건&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩 요구 조건은 requires안에 requires로 시작하는 표현식이 있는 것을 말합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655038009355&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;template&amp;lt;class T&amp;gt;
concept Semiregular = DefaultConstructible&amp;lt;T&amp;gt; &amp;amp;&amp;amp;
    CopyConstructible&amp;lt;T&amp;gt; &amp;amp;&amp;amp; Destructible&amp;lt;T&amp;gt; &amp;amp;&amp;amp; CopyAssignable&amp;lt;T&amp;gt; &amp;amp;&amp;amp;
requires(T a, size_t n)
{  
    requires Same&amp;lt;T*, decltype(&amp;amp;a)&amp;gt;; // 중첩 요구 조건
    requires Same&amp;lt;T*, decltype(new T)&amp;gt;; // 중첩 요구 조건
    requires Same&amp;lt;T*, decltype(new T[n])&amp;gt;; // 중첩 요구 조건
    { a.~T() } noexcept; // 복합 요구 조건
    { delete new T }; // 복합 요구 조건
    { delete new T[n] }; // 복합 요구 조건
};&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;미리 정의된 Concept&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;매번 concept를 만드는 것 보다는 미리 정의된 concept를 재사용하는 것을 권장합니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 미리 정의된 concept의 정의로 이동해서 구현 방식을 참고하는 것이 concept의 사용방법을 익히는데&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;많은 도움이 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 1039px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;이름&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 217px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 217px;&quot;&gt;std::three_way_comparable&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 217px;&quot;&gt;&amp;lt;=&amp;gt;연산자 &lt;br /&gt;&amp;nbsp; a &amp;lt;=&amp;gt; b 일때 a가 작으면 -1, 같으면 0, a가 크면 1을 리턴합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;다음의 조건을 만족하는지 체크합니다.&amp;nbsp;&lt;br /&gt;1. (a &amp;lt;=&amp;gt; b == 0) ==&amp;nbsp; bool(a == b)가 true&lt;br /&gt;2. (a &amp;lt;=&amp;gt; b != 0) ==&amp;nbsp; bool(a != b)가 true&lt;br /&gt;3. ( (a &amp;lt;=&amp;gt; b) &amp;lt;=&amp;gt; 0) 와 ( 0 &amp;lt;=&amp;gt; (a &amp;lt;=&amp;gt; b) )가 상등(equal)&lt;br /&gt;4. (a &amp;lt;=&amp;gt; b &amp;lt; 0) ==&amp;nbsp; bool(a &amp;lt; b)가 true&lt;br /&gt;5. (a &amp;lt;=&amp;gt; b &amp;gt; 0) ==&amp;nbsp; bool(a &amp;gt; b)가 true&lt;br /&gt;6. (a &amp;lt;=&amp;gt; b &amp;lt;= 0) ==&amp;nbsp; bool(a &amp;lt;= b)가 true&lt;br /&gt;7. (a &amp;lt;=&amp;gt; b &amp;gt;= 0) ==&amp;nbsp; bool(a &amp;gt;= b)가 true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::same_as&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;두 형식이 같은지 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 40px;&quot;&gt;std::derived_from&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 40px;&quot;&gt;template&amp;lt;class _Derived, class _Base&amp;gt; 형식의 콘셉트&lt;br /&gt;첫번째 인자가 두번째 인자의 파생 형식인지 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 74px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 74px;&quot;&gt;std::convertible_to&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 74px;&quot;&gt;template &amp;lt;class _From, class _To&amp;gt; 형식의 콘셉트&lt;br /&gt;첫번째 인자가 두번째 인자로 변환이 가능하지 체크&lt;br /&gt;std::convertible_to&amp;lt;char* , std::string&amp;gt; : true&amp;nbsp;&lt;br /&gt;std::convertible_to&amp;lt;std::string, char*&amp;gt; : false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::common_reference_with&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;두 형식을 어떤 공통의 참조 형식으로 변환 할 수 있음&lt;br /&gt;std::common_reference_with&amp;lt;T, U&amp;gt;일 때 T, U가 어떤 형식 참조형식인 C로 변환 가능 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 40px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 40px;&quot;&gt;std::common_with&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 40px;&quot;&gt;std::common_reference_with 비슷한데&amp;nbsp;&lt;br /&gt;참조형식이 아니고 값 형식이여도 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::assignable_from&amp;lt;T, U&amp;gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;std::assignable_from&amp;lt;_LTy, _RTy&amp;gt;&lt;br /&gt;_LTy이 참조 형식이어야 하고 _RTy을 _LTy으로 배정 할 수 있어야 합니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::swappable&amp;lt;T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;두 형식의 값을 교환 할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::integral&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;정수 형식(char, short, int, long, unsigned 형식)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::signed_integral&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;부호 있는 정수 형식(char, short, int, long,)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::unsigned_integral&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;부호 없는 정수 형식&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;(unsigned + char, short, int, long,)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::floating_point&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;부동 소수점 형식(float, double, long double)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::destructible&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;소멸자 사용 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::constructible_from&amp;lt;T, ...Args&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;std::constructible_from&amp;lt;_Ty, ... _ArgTys&amp;gt; 형식으로 _Ty 객체를 생성할 수 있는지 체크&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::default_initializable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;객체&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;기본 생성자 호출 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::move_constructible&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;객체 &lt;/span&gt;이동 생성자 호출 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::copy_constructible&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;객체&lt;/span&gt;&amp;nbsp;&lt;/span&gt;복사 생성자 호출 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::equality_comparable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;객체 상등 비교 가능 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 37px;&quot;&gt;std::totally_ordered&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 37px;&quot;&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;전 순서 집합 가능 여부&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::moveable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;이동 가능 여부&lt;br /&gt;is_object_v &amp;amp;&amp;amp; move_constructible &amp;amp;&amp;amp; assignable_from&amp;lt;T&amp;amp;, T&amp;gt; &amp;amp;&amp;amp; swappable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::copyable&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;복사 가능 여부&lt;br /&gt;copy_constructible &amp;amp;&amp;amp; movable &amp;amp;&amp;amp; assignable_from&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&amp;lt;T&amp;amp;, T&amp;amp;&amp;gt;, &lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&amp;lt;T&amp;amp;, const T&amp;gt; &lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&amp;lt;T&amp;amp;, const T&amp;amp;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::semiregular&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;준 정규 형식 여부&lt;br /&gt;copyable &amp;amp;&amp;amp; default_initializable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 37px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 37px;&quot;&gt;std::regular&amp;lt;T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 37px;&quot;&gt;정규 형식 여부&lt;br /&gt;semiregular &amp;amp;&amp;amp; equality_comparable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::invocable&amp;lt;F, ...Args&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;std::invoke를 호출 할 수 있는지 여부&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::regular_invocable&amp;lt;F, ...Args&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;invocable에 상등을 보존하고 함수 인수들을 수정하지 않습니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::predicate&amp;lt;F, T, U&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;invocable하며 bool 값을 돌려 줍니다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::input_iterator&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;입력 반복자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 20px;&quot;&gt;std::output_iterator&amp;lt;It, T&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 20px;&quot;&gt;출력 반복자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::forward_iterator&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;순방향(전진) 반복자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::bidirectional_iterator&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;양방향 반복자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::random_access_iterator&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;임의 접근 반복자&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::contiguous_iterator&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;연속 반복자&lt;br /&gt;연속 반복자를 지원하려면 컨테이너 요소들을 메모리에 연속해서 저장 해야 합니다.&lt;br /&gt;(std::array, std::vector, std::string)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::permutable&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;요소들이 제자리 순서 변경이 가능함&lt;br /&gt;std::forward_iterator&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::mergeable&amp;lt;It1, It2, Out&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;정렬된 순차열들을 병합해서 출력 순차열로 산출 할 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 30.2327%; height: 17px;&quot;&gt;std::sortable&amp;lt;It&amp;gt;&lt;/td&gt;
&lt;td style=&quot;width: 69.7673%; height: 17px;&quot;&gt;요소들의 순서를 변경해서 정렬된 순차열을 만들 수 있음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>c++/modern c++</category>
      <category>auto</category>
      <category>c++20</category>
      <category>Concept</category>
      <category>template</category>
      <category>콘셉트</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/99</guid>
      <comments>https://jungwoong.tistory.com/99#entry99comment</comments>
      <pubDate>Fri, 10 Jun 2022 23:11:11 +0900</pubDate>
    </item>
    <item>
      <title>[SonarQube] SonarQube 설치 하기</title>
      <link>https://jungwoong.tistory.com/98</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;사전 필요 작업&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Java JDK SE 11버전을 설치합니다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;윈도우&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.oracle.com/kr/java/technologies/javase/jdk11-archive-downloads.html&quot;&gt;https://www.oracle.com/kr/java/technologies/javase/jdk11-archive-downloads.html&lt;/a&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;우분투&lt;/h4&gt;
&lt;pre id=&quot;code_1647481017368&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt install openjdk-11-jdk-headless

export JAVA_HOME=/usr/lib/jvm/java-1.11.0-openjdk-amd64
# es를 위한 JAVA HOME 설정
export ES_JAVA_HOME=/usr/lib/jvm/java-1.11.0-openjdk-amd64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;PostgreSQL 설치&lt;/h3&gt;
&lt;pre id=&quot;code_1647940988752&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;sudo apt-get update
sudo apt-get install postgresql postgresql-contrib

# postgres 계정으로 변환
sudo -i -u postgres

# postreSQL 실행
psql

# 유저 생성
$ CREATE USER sonarqube with PASSWORD 'sonarqube';
$ ALTER ROLE sonarqube WITH createdb;

# 스키마 생성
$ CREATE DATABASE sonarqube owner sonarqube;
# 스키마에 권한 부여
$ GRANT ALL PRIVILEGES ON DATABASE sonarqube TO sonarqube;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설치 방법&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SonarQube 버전 : 8.9(LTS)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;윈도우 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;윈도우 설치 방법&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;소나 큐브를 다운로드 합니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;h&lt;a href=&quot;https://www.sonarqube.org/downloads/&quot;&gt;ttps://www.sonarqube.org/downloads/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다운로드한 파일의 압축을 해제 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;리눅스 환경&lt;/h3&gt;
&lt;pre id=&quot;code_1647941103495&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 파일을 다운 받습니다.(LTS)
wget https://binaries.sonarsource.com/Distribution/sonarqube/sonarqube-8.9.7.52159.zip
// 압축을 풀기 위한 작업 
sudo apt-get install unzip
unzip sonarqube-8.9.7.52159.zip&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설정 및 실행&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;주요 폴더 설명&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$SONARQUBE-HOME은 SonarQube가 설치된 폴더를 말합니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.1861%;&quot;&gt;경로&lt;/td&gt;
&lt;td style=&quot;width: 70.8139%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.1861%;&quot;&gt;$SONARQUBE-HOME/bin&lt;/td&gt;
&lt;td style=&quot;width: 70.8139%;&quot;&gt;SonarQube 실행 파일 폴더&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.1861%;&quot;&gt;$SONARQUBE-HOME/conf&lt;/td&gt;
&lt;td style=&quot;width: 70.8139%;&quot;&gt;SonarQube 설정 파일 폴더&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.1861%;&quot;&gt;$SONARQUBE-HOME/data&lt;/td&gt;
&lt;td style=&quot;width: 70.8139%;&quot;&gt;SonarQube 데이터 파일 폴더&lt;br /&gt;es나 웹의 플러그인 정보의 default 저장 폴더&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 29.1861%;&quot;&gt;$HOME_SONARQUBE/log&lt;/td&gt;
&lt;td style=&quot;width: 70.8139%;&quot;&gt;SonarQube 로그 폴더&lt;br /&gt;sonar.&amp;lt;날짜&amp;gt;.log : sonarqube에 대한 로그 정보&lt;br /&gt;es.log : elasticsearch에 대한 로그 정보&lt;br /&gt;web.log : 웹서버에 대한 로그 정보&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;설정 하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;$HOME_SONARQUBE/conf 폴더의 sonar.properties 파일을 수정합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647941486061&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# PostgreSQL를 설정합니다. 
# Example for PostgreSQL
sonar.jdbc.username=sonarqube
sonar.jdbc.password=mypassword
sonar.jdbc.url=jdbc:postgresql://localhost/sonarqube

# Elasticsearch 저장소를 설정합니다.
# 기본 값은 $SONARQUBE-HOME/data 입니다.
sonar.path.data=/var/sonarqube/data
sonar.path.temp=/var/sonarqube/temp

# 웹 설정을 변경합니다.
# 기본 포트 값은 9000이고 context paths는 &quot;/&quot;입니다.
sonar.web.host=127.0.0.1
sonar.web.port=9000
sonar.web.context=&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;실행 하기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$HOME_SONARQUBE/bin/ 에서 os에 맞춰서 실행합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본으로 실행 하였다면 &lt;a href=&quot;http://bard-sonarcube.cloud.ncsoft.com:9000&quot;&gt;http://localhost:9000&lt;/a&gt; 으로 접속 할 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 접속 정보는 admin/admin 입니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;유저 관리&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;admin 계정으로 접속하면 Administration -&amp;gt; Security -&amp;gt; Users를 통해서 유저를 추가할 수 있습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 연동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트는 젠킨스를 통해서 연동되거나 프로젝트 자체로 연동 할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참조&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.sonarqube.org/8.9/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.sonarqube.org/8.9/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>TOOL</category>
      <category>SonarQube</category>
      <category>정적코드분석</category>
      <category>코드분석</category>
      <category>코드스멜</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/98</guid>
      <comments>https://jungwoong.tistory.com/98#entry98comment</comments>
      <pubDate>Tue, 22 Mar 2022 21:56:55 +0900</pubDate>
    </item>
    <item>
      <title>[MongoDB] 설치 및 삭제</title>
      <link>https://jungwoong.tistory.com/96</link>
      <description>&lt;p&gt;이번 글에서는 MongoDB를 설치하는 방법에 대해서 공유합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;현재 최신 버전인 mongoDB 4.4를 설치합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;리눅스(ubuntu 18.04)&lt;/h2&gt;
&lt;p&gt;하단에 있는 mongoDB의 공식 사이트를 설치 방법을 참조하시는 것이 좋습니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;설치 하기&lt;/h3&gt;
&lt;pre id=&quot;code_1615194924681&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// ubuntu 18.2 &amp;amp; mongoDB 4.4
 
// mongoDB의 public key를 등록합니다. 
wget -qO - https://www.mongodb.org/static/pgp/server-4.4.asc | sudo apt-key add -
 
// mongoDB의 4.4를 생성하기 위한 리스트 파일 설정
echo &quot;deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.4 multiverse&quot; | sudo tee /etc/apt/sources.list.d/mongodb-org-4.4.list
 
// 로컬 패키지 데이터 베이스 업데이트
sudo apt-get update
 
// 최신버전의 mongoDB 설치
sudo apt-get install -y mongodb-org
 
// 서비스 시작
sudo systemctl start mongod

# mongoDB 종료
mongo localhost/admin -u &amp;lt;유저명&amp;gt; -p &amp;lt;비밀번호&amp;gt;
 
&amp;gt; use admin
 
# localhost로 접속해야 됨
&amp;gt; db.shutdownServer();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삭제 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1615195093776&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// apt로 설치된 mongoDB 확인
sudo apt list --installed | grep mongo
 
// 제거 ex) sudo apt remove mongodb-server
 
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;참조&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/&quot;&gt;https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;공 통&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;몽고 디비 설정 파일&amp;nbsp;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.mongodb.com/manual/reference/configuration-options/index.html&quot;&gt;https://docs.mongodb.com/manual/reference/configuration-options/index.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몽고 DB의 설정 파일은 YAML 포맷으로 되어 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;경 로&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.496%;&quot;&gt;플랫폼&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%;&quot;&gt;파일 위치&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.496%;&quot;&gt;Linux&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%;&quot;&gt;/etc/mongod.conf&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.496%;&quot;&gt;macOS&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%;&quot;&gt;/usr/local/etc/mongod.conf&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.496%;&quot;&gt;Windows&lt;/td&gt;
&lt;td style=&quot;width: 52.1706%;&quot;&gt;&amp;lt;install directory&amp;gt;\bin\mongod.cfg&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;중요 옵션&lt;/h3&gt;
&lt;p&gt;자주 사용하는 옵션&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;타입&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;기본값&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;net.bindIp&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;localhost&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;ip address 정보&lt;/p&gt;
&lt;p&gt;ex) 0.0.0.0&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;net.port&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;
&lt;p&gt;27017 - mongod&lt;br /&gt;&lt;span&gt;27018 - shard member&lt;br /&gt;&lt;/span&gt;&lt;span&gt;27019 - config server member&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;MongoDB의 포트번호&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;processManagement.fork&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;백그라운드에서 실행하도록 mongod에 데몬 모드를 설정합니다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;processManagement.pidFilePath&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;경로&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;mongod의 프로세스 ID를 저장할 경로를 지정합니다.&amp;nbsp;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;processManagement.timeZoneInfo&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;/usr/share/zoneinfo&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;replication.enableMajorityReadConcern&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;Read Concern은 아래와 같은 타입이 있음&lt;br /&gt;local : 본인의 NODE 확인&lt;br /&gt;majority : 다수의 NODE가 동의&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;replication.oplogSizeMB&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;사용 가능한 disk의 5% 공간&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;replication 작업을 위한 로그 사이즈의 최대 크기 설정 (MB단위)&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;replication.replSetName&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;mongod 가 속한 replication 셋트의 이름입니다.&lt;br /&gt;replication set에 속한 모든 인스턴스들은 동일한 값을 가집니다.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.destination&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;&quot;file&quot; or &quot;syslog&quot;&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;file :&amp;nbsp;systemLog.path 경로에 파일을 기록합니다.&lt;br /&gt;file 옵션을 추천하는 이유는 syslog의 경우 timestamp를 syslog가 생성하기 떄문에&lt;br /&gt;로그가 생성된 시간하고 불일치 할 수 있음(특히 부하가 심한 경우)&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.logRotate&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;rename&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;rename : 로그 파일의 이름을 변경합니다.&amp;nbsp;&lt;br /&gt;reopen : 일반적인 linux/unix의 동작 방식에 따라 로그파일을 관리합니다.&lt;br /&gt;reopen을 사용하는경우 logAppend도 true로 설정해야 함.&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.path&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;/var/log/mongodb/mongod.log&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;로그가 기록되는 경로&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.quiet&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;mongos 또는 mongod를 조용한 모드로 실행합니다.&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.syslogFacility&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;user&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;syslog에 기록되는 메시지를 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.traceAllExceptions&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;디버깅을 위한 자세한 정보를 출력&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.2558%;&quot;&gt;systemLog.verbosity&lt;/td&gt;
&lt;td style=&quot;width: 26.279%;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 40.3489%;&quot;&gt;
&lt;p&gt;로그 상세 수준을 설정합니다.&amp;nbsp;&lt;br /&gt;디버그 메시지를 추가하려면 1 ~ 5 사이 값을 설정해야합니다.&amp;nbsp;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Mongo 계정 추가 방법&lt;/h3&gt;
&lt;pre id=&quot;code_1615195034126&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 클라이언트로 접속
mongo
 
// admin으로 변경
&amp;gt; use admin
 
// pbnadmin 계정 추가
&amp;gt; db.createUser({user : &quot;admin&quot;, pwd:&quot;12345&quot;, roles :['root'], mechanisms : [&quot;SCRAM-SHA-1&quot;]})&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>NOSQL/mongoDB</category>
      <category>MongoDB</category>
      <category>ubuntu 18</category>
      <category>서비스 동작</category>
      <category>설정 옵션</category>
      <category>설치</category>
      <author>웅웅이님</author>
      <guid isPermaLink="true">https://jungwoong.tistory.com/96</guid>
      <comments>https://jungwoong.tistory.com/96#entry96comment</comments>
      <pubDate>Mon, 8 Mar 2021 18:18:26 +0900</pubDate>
    </item>
  </channel>
</rss>