{"id":402119,"date":"2024-06-29T16:18:05","date_gmt":"2024-06-29T16:18:05","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=402119"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T16:18:05","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=402119","title":{"rendered":"<span>An article for those who, like me, do not understand the purpose of std::common_type<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This article investigates why the standard library needs a way to deduce a common type, how it is implemented and how it works.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/6t\/i4\/u_\/6ti4u_y4lpgp4i7btgmy9tfz3z0.png\" alt=\"common_type\/image1.png\" data-src=\"https:\/\/habrastorage.org\/webt\/6t\/i4\/u_\/6ti4u_y4lpgp4i7btgmy9tfz3z0.png\"\/><\/p>\n<p><a name=\"habracut\"><\/a>  <\/p>\n<p>To begin with, I&#8217;d like to thank my teammate <a href=\"https:\/\/habr.com\/ru\/users\/khandeliants\/\" class=\"user_link\">khandeliants<\/a>. He helped me figure out some things in the C++ standard that I found ambiguous. He also helped me refine my code examples.<\/p>\n<p>  <\/p>\n<h2 id=\"ways-stdcommon_type-was-useful-for-us\">Ways std::common_type was useful for us<\/h2>\n<p>  <\/p>\n<p>It all started when the PVS-Studio team set out to sift through and majorly enhance the C++ analyzer&#8217;s core. Currently, one of the big tasks is to implement a new type system. Right now, our type system consists of strings encoded in a specific way. We want to replace this system with a hierarchical one. I won&#8217;t go into too much detail about the new type system. Loosely put, we are trying to turn this:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/1p\/u0\/jd\/1pu0jd7oflo88_dbvblj4siiseq.png\" alt=\"common_type\/image2.png\" data-src=\"https:\/\/habrastorage.org\/webt\/1p\/u0\/jd\/1pu0jd7oflo88_dbvblj4siiseq.png\"\/><\/p>\n<p>  <\/p>\n<p>into this:<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/sq\/sk\/uz\/sqskuztub-anpxr_qfstmfvgc_c.png\" alt=\"common_type\/image3.png\" data-src=\"https:\/\/habrastorage.org\/webt\/sq\/sk\/uz\/sqskuztub-anpxr_qfstmfvgc_c.png\"\/><\/p>\n<p>  <\/p>\n<p>If you want to know more about it, check out the <a href=\"https:\/\/www.youtube.com\/watch?app=desktop&amp;v=qwnJWPuLNwo\">talk<\/a> my teammate Yuri gave at the <a href=\"https:\/\/italiancpp.github.io\/\">itCppCon21<\/a> conference. There he discussed our old and new type systems in great detail \u2014 and showed funny pictures. By now, I think, he&#8217;s assembled enough material for two or three new talks. So, we can all start looking forward to them \ud83d\ude42<\/p>\n<p>  <\/p>\n<p>The new type system uses analogs of <em><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/header\/type_traits\">type_traits<\/a><\/em>. These custom traits, same as their predecessors, help modify types and get the necessary information on types.<\/p>\n<p>  <\/p>\n<p>Just recently I wrote an <em><a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/meta.trans.other\">std::common_type<\/a><\/em> implementation for our type system. The original std::common_type trait is often used in metaprogramming to process an arbitrary number of types passed \u2014 and deduce a common type for them. We found our custom trait helpful when we needed to deduce a resulting type \u2014 for example, when we come across an arithmetic operation in a binary expression:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">if (operationInfo->m_type == OperatorType::Arithmetic) {   auto leftType  = TypeTraits::ExtractMemberType                    (result->GetLeftOperand().GetType());   auto rightType = TypeTraits::ExtractMemberType                    (result->GetRightOperand().GetType());   auto resType = Types::Traits::CommonType(leftType, rightType);   .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Before, this operation required much more code. Now the code looks elegant.<\/p>\n<p>  <\/p>\n<h2 id=\"why-c-developers-need-stdcommon_type-at-all\">Why C++ developers need std::common_type at all<\/h2>\n<p>  <\/p>\n<p>Suppose we want to write a naive implementation of a function template in order to calculate two vectors&#8217; dot product. These vectors can be instantiated with various types passed to them. The dot product type must be deduced automatically. In C++14 and later, one of the ways to implement such a function template is as follows:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;vector>  template &lt;typename T, typename U> auto dot_product(const std::vector&lt;T> &amp;a, const std::vector&lt;U> &amp;b) {   \/\/ some bounds checks    ??? result {};    auto a_it = a.begin();   auto b_it = b.begin();    while (a_it != a.end())   {     result += static_cast&lt;???>(*a_it++) * static_cast&lt;???>(*b_it++);   }    return result; }<\/code><\/pre>\n<p>  <\/p>\n<p>The scenario assumes that the function receives vectors of the same size. Otherwise, calculating the dot product is impossible and will produce an array out-of-bounds error.<\/p>\n<p>  <\/p>\n<p>So, the function does exactly what we intended it to do. The compiler deduces for us the resulting type from the <em>return statement<\/em>. Only one problem remains \u2014 we somehow need to deduce the common type for the <em>result<\/em> variable.<\/p>\n<p>  <\/p>\n<p>However, before writing any code, let&#8217;s study one very interesting language construct \u2014 the ternary operator. Maybe it can help us with this task.<\/p>\n<p>  <\/p>\n<h2 id=\"conditional-operator\">Conditional operator<\/h2>\n<p>  <\/p>\n<p>Since the standard describes the ternary operator in <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/expr.cond\">great detail<\/a>, covering the operator&#8217;s every aspect here seems excessive. So, I&#8217;ll focus on the most common cases that involve type deduction.<\/p>\n<p>  <\/p>\n<p>To help you understand the scenarios and results better, I&#8217;ll use the following to help me present them:<\/p>\n<p>  <\/p>\n<ul>\n<li>A variadic template of the <em>tp<\/em> class, with no definition: enables you to use the compilation error to find out the resulting type.<\/li>\n<li>Clang AST: shows the program&#8217;s abstract syntax tree.<\/li>\n<\/ul>\n<p>  <\/p>\n<p>Alright, let&#8217;s get our hands dirty and look at some scenarios.<\/p>\n<p>  <\/p>\n<h3 id=\"case-1\">Case 1<\/h3>\n<p>  <\/p>\n<p>If the second and third operands are both of type <em>void<\/em>, then the result is also of type <em>void<\/em>. This is possible if both expressions contain, for example, <em>throw<\/em>, or calls to functions that return <em>void<\/em>, or explicit conversion to the <em>void<\/em> type. Below is some code that demonstrates this, with <a href=\"https:\/\/godbolt.org\/z\/jTeT8EG1K\">messages the compiler prints<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">void foo(); void bar();  int foobar(); float barfoo();  template &lt;typename ...> struct tp;              \/\/ type printer  void examples(bool flag) {   tp&lt;decltype(flag ? foo() : bar()),                     \/\/ void      decltype(flag ? (void) foobar() : (void) barfoo()), \/\/ void      decltype(flag ? throw 0 : throw 3.14)> _;           \/\/ void }<\/code><\/pre>\n<p>  <\/p>\n<p>If the second or third operand is a <em>throw<\/em> expression, then the resulting type is deduced from the other operand. In this case, the other operand must be of some type other than <em>void<\/em>. The code below demonstrates this scenario, with <a href=\"https:\/\/godbolt.org\/z\/n9GsrGf8K\">messages the compiler prints<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">char arr[16];  template &lt;typename ...> struct tp;              \/\/ type printer  void examples(bool flag) {   tp&lt;decltype(flag ? nullptr : throw \"abs\"), \/\/ nullptr_t      decltype(flag ? 3.14 : throw 3.14),     \/\/ double      decltype(flag ? arr : throw 3.14)> _;   \/\/ char (&amp;)[16] }<\/code><\/pre>\n<p>  <\/p>\n<h3 id=\"case-2\">Case 2<\/h3>\n<p>  <\/p>\n<p>If operand two and three are of different types and one of them is of a class type, the compiler chooses an overload that produces operands of the same types. For example, the compiler may choose a <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/class.conv\">converting constructor<\/a> or an <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/class.conv\">implicit conversion operator<\/a>. This is shown in the code below, with <a href=\"https:\/\/godbolt.org\/z\/5ozqafszT\">printed compiler messages<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;typename ...> struct tp;              \/\/ type printer  struct IntWrapper {   IntWrapper(int)    {     \/\/ ....   } };  void examples(bool flag) {   tp&lt;decltype(flag ? IntWrapper {42} : 42)> _; }<\/code><\/pre>\n<p>  <\/p>\n<p>If you take a look at the AST that Clang built for this code, you can notice the following:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">....  -FunctionDecl &lt;line:9:1, line:12:1> line:9:6 foo 'IntWrapper (bool)'  |-ParmVarDecl &lt;col:10, col:15> col:15 used b 'bool'  `-CompoundStmt &lt;line:10:1, line:12:1>    `-ReturnStmt &lt;line:11:3, col:34>      `-ConditionalOperator &lt;col:10, col:34> 'IntWrapper'        |-ImplicitCastExpr &lt;col:10> 'bool' &lt;LValueToRValue>        | `-DeclRefExpr &lt;col:10> 'bool' lvalue ParmVar 0x558edcfc99d8 'b' 'bool'        |-CXXTemporaryObjectExpr &lt;col:14, col:30> 'IntWrapper' 'void (int)' list        | `-IntegerLiteral &lt;col:27> 'int' 42        `-ImplicitCastExpr &lt;col:34> 'IntWrapper' &lt;ConstructorConversion>   \/\/ &lt;=          `-CXXConstructExpr &lt;col:34> 'IntWrapper' 'void (int)'           `-IntegerLiteral &lt;col:34> 'int' 42                              \/\/ &lt;=<\/code><\/pre>\n<p>  <\/p>\n<p>Here Clang implicitly calls a converting constructor for the third operand, and consequently, both operands become of the same type \u2014 <em>IntWrapper<\/em>.<\/p>\n<p>  <\/p>\n<h3 id=\"case-3\">Case 3<\/h3>\n<p>  <\/p>\n<p>This scenario involves the second and third operands with <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv\">standard conversions<\/a> applied: <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.lval\">lvalue-to-rvalue<\/a>, <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.array\">array-to-pointer<\/a>, or <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.func\">function-to-pointer<\/a>. After the conversions are executed, several situations are possible.<\/p>\n<p>  <\/p>\n<p>If the second and third operands are of the same type, the resulting type will be the same. The codebelow demonstrates this, with <a href=\"https:\/\/godbolt.org\/z\/4z38sPccT\">messages the compiler prints<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;typename ...> struct tp;              \/\/ type printer  struct MyClass {   \/\/ .... };  void examples(bool flag) {   tp&lt;decltype(flag ? MyClass {} : MyClass {})> _; }<\/code><\/pre>\n<p>  <\/p>\n<p>The second and third operands can also have an arithmetic type or an enumeration type. For arithmetic and enumeration types, the <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/expr.arith.conv\">usual arithmetic conversions<\/a> form the common type. This common type is the resulting type. The code below demonstrates this, with <a href=\"https:\/\/godbolt.org\/z\/zMKK39rKT\">printed compiler messages<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;typename ...> struct tp;              \/\/ type printer  void examples(bool flag) {   char ch                = 1;   short sh               = 2;   double d               = 3;   float f                = 4;   unsigned long long ull = 5;   long double ld         = 6;    tp&lt;decltype(flag ? ch :  sh),      decltype(flag ? f :  d),      decltype(flag ? ull : ld) > _; }<\/code><\/pre>\n<p>  <\/p>\n<p>Note that one or both operands can be of type pointer or of type pointer-to-member. In this case a <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/expr.type\">composite pointer type<\/a> is formed and it becomes the resulting type. The following rules are used to form it: <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.ptr\">pointer conversions<\/a>\/<a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.mem\">pointer-to-member conversions<\/a>, <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.fctptr\">function pointer conversions<\/a> and <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/conv.qual\">qualification conversions<\/a>. This is what it looks like, with <a href=\"https:\/\/godbolt.org\/z\/3e35YcTcf\">printed compiler messages<\/a>:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;typename ...> struct tp;              \/\/ type printer  struct MyBaseClass {   \/\/ .... };  struct MyClass : MyBaseClass {   \/\/ .... };  void examples(bool flag) {   auto a = new MyClass();   auto b = new MyBaseClass();   tp&lt;decltype(flag ? a : b)> _; }<\/code><\/pre>\n<p>  <\/p>\n<p>Also, both operands can be of type <em><a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/lex.nullptr\">std::nullptr_t<\/a><\/em>. Or one operand can be of type <em>std::nullptr_t<\/em>, and the other one is <em>nullptr<\/em>. Then the resulting type is <em>std::nullptr_t<\/em>. This is what the code looks like, with <a href=\"https:\/\/godbolt.org\/z\/qfsKrqGce\">printed compiler messages:<\/a><\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;cstddef>  template &lt;typename ...> struct tp;              \/\/ type printer  void examples(bool flag) {    tp&lt;decltype(flag ? std::nullptr_t {} : nullptr )> _; }<\/code><\/pre>\n<p>  <\/p>\n<p>Now we can see that deducing a common type is very easy \u2014 and in most cases the ternary operator can help. Well, enough theory. Let&#8217;s use the principles described above and write some code that deduces a common type!<\/p>\n<p>  <\/p>\n<p>P.S. In order to write a custom <em>std::common_type<\/em> trait implementation for our new type system (<em>TypeTraits::CommonType<\/em>), we needed to use all common type deduction rules described above, and some that we haven&#8217;t mentioned.<\/p>\n<p>  <\/p>\n<h2 id=\"writing-a-custom-common_type\">Writing a custom common_type<\/h2>\n<p>  <\/p>\n<p>Let&#8217;s get back to our function that calculates a dot product of vectors. Starting with C++11, we can use the <em><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/language\/decltype\">decltype<\/a><\/em> specifier that takes an expression and returns this expression&#8217;s type. We&#8217;ve already used this specifier earlier \u2014 when we worked with <em>type_printer<\/em>. From the previous paragraph we know, that if decltype receives a ternary operator call with objects of two types, the compiler deduces the common type.<\/p>\n<p>  <\/p>\n<p>Let&#8217;s try it:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;vector>  template &lt;typename T, typename U> auto dot_product(const std::vector&lt;T> &amp;a, const std::vector&lt;U> &amp;b) {   \/\/ ....   decltype(true ? std::declval&lt;T>() : std::declval&lt;U>()) result {};   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Let&#8217;s take a closer look at what this code does:<\/p>\n<p>  <\/p>\n<ol>\n<li>The <em>decltype<\/em> specifier deduces the type of the expression in the parentheses.<\/li>\n<li>Inside <em>decltype<\/em>, we use the ternary operator. Any expression can be the first operand, for example, <em>true<\/em>.<\/li>\n<li>Then the passed template types are substituted for the second and third operands. There is only one problem \u2014 the ternary operator operates expressions. So let&#8217;s create objects through <em><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/utility\/declval\">std::declval<\/a><\/em>.<\/li>\n<\/ol>\n<p>  <\/p>\n<p><em>std::declval&lt;T><\/em> is a function template with <strong>no implementation<\/strong>. This template returns an <em>rvalue<\/em>-link to type <em>T<\/em>. When <em>T=void<\/em>, the expression returns the <em>void<\/em> type. This template is often used in compile-time context (<em>decltype<\/em>, <em>sizeof<\/em>, <em>requires<\/em>, &#8230;.) and allows working with an object of the passed type and avoid the constructor call. This is especially useful if the <em>T<\/em> type does not have a default public constructor or if this constructor has been removed.<\/p>\n<p>  <\/p>\n<p>Note that as a type, you may get references. In this case <em><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/types\/decay\">std::decay<\/a><\/em> comes in handy. It removes CV-qualifiers and references. It adds pointers for functions (<em>function-to-pointer conversion<\/em>) and converts arrays to pointers (<em>array-to-pointer conversion<\/em>):<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;vector>  template &lt;typename T, typename U> auto dot_product(const std::vector&lt;T> &amp;a, const std::vector&lt;U> &amp;b) {   \/\/ ....   std::decay_t&lt;         decltype( true ? std::declval&lt;typename std::decay&lt;T>::type>()                        : std::declval&lt;typename std::decay&lt;U>::type>()         )   > result {};   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Agree \u2014 most people wouldn&#8217;t want to write this in their code. Let&#8217;s try to refactor the code a little. To do this, we&#8217;ll need to write a couple of helper class templates for convenience. First, let&#8217;s try to write a class that deduces a common type for two types passed:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">template &lt;class T, class U> struct common_type {   using type = std::decay_t&lt;       decltype( true ? std::declval&lt; std::decay_t&lt;T> >()                      : std::declval&lt; std::decay_t&lt;U> >() ) >; };<\/code><\/pre>\n<p>  <\/p>\n<p>Now we can use this <em>common_type<\/em> in our code:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;vector>  template &lt;typename T, typename U> auto dot_product(const std::vector&lt;T> &amp;a, const std::vector&lt;U> &amp;b) {   \/\/ ....   common_type&lt;T, U>::type result {};   \/\/ .... }<\/code><\/pre>\n<p>  <\/p>\n<p>Excellent, we got rid of all this scary bunch of code and made the code easy to read. Now it&#8217;s time to teach <em>common_type<\/em> to work with any number of types passed \u2014 from zero to n. Let&#8217;s slightly change our basic class template and its specializations:<\/p>\n<p>  <\/p>\n<pre><code class=\"cpp\">#include &lt;type_traits>  template &lt;typename ...> struct common_type; \/\/ (1)  template &lt;typename ...Ts> using common_type_t = typename common_type&lt;Ts...>::type;  template &lt;> struct common_type&lt;> \/\/ (2) { };  template &lt;class T> struct common_type&lt;T> \/\/ (3) {   using type = std::decay_t&lt;T>; };  template &lt;class T, class U> struct common_type&lt;T, U>         \/\/ (4) {   using type = std::decay_t&lt;       decltype( true ? std::declval&lt; std::decay_t&lt;T> >()                      : std::declval&lt; std::decay_t&lt;U> >() ) >; };  template &lt;class T, class U, class... V> struct common_type&lt;T, U, V...>         \/\/ (5) {   using type =  typename common_type                &lt;typename common_type&lt;T,U>::type, V...>::type; };<\/code><\/pre>\n<p>  <\/p>\n<p>It&#8217;s worth mentioning that <em>common_type<\/em> is implemented in the standard library in a similar way. Now let&#8217;s examine the code above and see what happens there: <\/p>\n<p>  <\/p>\n<ol>\n<li>The primary variadic class template is declared.<\/li>\n<li>For an empty list of template arguments, we declare an explicit template specialization that does not contain anything.<\/li>\n<li>For one template argument, we declare a partial template specialization that contains this type after the <em><a href=\"https:\/\/en.cppreference.com\/w\/cpp\/types\/decay\">std::decay<\/a><\/em> trait is performed. This trait removes CV-qualifiers, links, decays functions into pointers (<em>function-to-pointer conversion<\/em>), and converts arrays to pointers (<em>array-to-pointer conversion<\/em>).<\/li>\n<li>For two template arguments, we declare a partial specialization that infers the resulting type based on the type inference rules of the conditional operator, applying the <em>std::decay<\/em> trait to the passed arguments beforehand. <\/li>\n<li>For three or more template arguments, we declare a partial specialization that first retrieves the common type for the first two arguments. It uses the specialization for 2 types to do this. Then it instantiates itself recursively, passing the common type for the first pair of types and the rest of the template parameter pack as template arguments. Overall, <em>common_type&lt;a, b, c, d><\/em> is equivalent to <em>common_type&lt;common_type&lt;common_type&lt;a, b>, c>, d><\/em>. See an example on <a href=\"https:\/\/cppinsights.io\/lnk?code=I2luY2x1ZGUgPHR5cGVfdHJhaXRzPgoKdGVtcGxhdGUgPHR5cGVuYW1lIC4uLj4Kc3RydWN0IGNvbW1vbl90eXBlOyAvLyAoMSkKCnRlbXBsYXRlIDx0eXBlbmFtZSAuLi5Ucz4KdXNpbmcgY29tbW9uX3R5cGVfdCA9IHR5cGVuYW1lIGNvbW1vbl90eXBlPFRzLi4uPjo6dHlwZTsKCnRlbXBsYXRlIDw+CnN0cnVjdCBjb21tb25fdHlwZTw+IC8vICgyKQp7Cn07Cgp0ZW1wbGF0ZSA8Y2xhc3MgVD4Kc3RydWN0IGNvbW1vbl90eXBlPFQ+IC8vICgzKQp7CiAgdXNpbmcgdHlwZSA9IHN0ZDo6ZGVjYXlfdDxUPjsKfTsKCnRlbXBsYXRlIDxjbGFzcyBULCBjbGFzcyBVPgpzdHJ1Y3QgY29tbW9uX3R5cGU8VCwgVT4gICAgICAgICAvLyAoNCkKewogIHVzaW5nIHR5cGUgPSBzdGQ6OmRlY2F5X3Q8CiAgICAgIGRlY2x0eXBlKCB0cnVlID8gc3RkOjpkZWNsdmFsPCBzdGQ6OmRlY2F5X3Q8VD4gPigpCiAgICAgICAgICAgICAgICAgICAgIDogc3RkOjpkZWNsdmFsPCBzdGQ6OmRlY2F5X3Q8VT4gPigpICkgPjsKfTsKCnRlbXBsYXRlIDxjbGFzcyBULCBjbGFzcyBVLCBjbGFzcy4uLiBWPgpzdHJ1Y3QgY29tbW9uX3R5cGU8VCwgVSwgVi4uLj4gICAgICAgICAvLyAoNSkKewogIHVzaW5nIHR5cGUgPSB0eXBlbmFtZSBjb21tb25fdHlwZTx0eXBlbmFtZSBjb21tb25fdHlwZTxULFU+Ojp0eXBlLCBWLi4uPjo6dHlwZTsKfTsKCnRlbXBsYXRlCnN0cnVjdCBjb21tb25fdHlwZTxjaGFyLCBzaG9ydCwgaW50Pjs=&amp;insightsOptions=cpp2a&amp;std=cpp2a&amp;rev=1.0\">C++ Insights<\/a>.<\/li>\n<\/ol>\n<p>  <\/p>\n<p>As I said above, the complete information about the ternary operator&#8217;s type inference is available in the standard. I used the latest up-to-date working draft. You can find this information in chapter <a href=\"https:\/\/eel.is\/c++draft\/expr.cond\">7.6.16<\/a>. The drafts themselves are available, for example, <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/standards\">here.<\/a> You can also use documentation from <a href=\"https:\/\/en.cppreference.com\/w\/\">cppreference<\/a>.<\/p>\n<p>  <\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>  <\/p>\n<p>In this article I&#8217;ve reviewed how <em>std::common_type<\/em> works. For a better understanding, we read the standard and wrote the trait&#8217;s implementation \u2014 we even discussed the ternary operator&#8217;s logic. I hope you find this article useful. Thank you for reading!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/592213\/\"> https:\/\/habr.com\/ru\/articles\/592213\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-1\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>This article investigates why the standard library needs a way to deduce a common type, how it is implemented and how it works.<\/p>\n<p>  <\/p>\n<p><img decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/6t\/i4\/u_\/6ti4u_y4lpgp4i7btgmy9tfz3z0.png\" alt=\"common_type\/image1.png\" data-src=\"https:\/\/habrastorage.org\/webt\/6t\/i4\/u_\/6ti4u_y4lpgp4i7btgmy9tfz3z0.png\"\/><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-402119","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/402119","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=402119"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/402119\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=402119"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=402119"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=402119"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}