{"id":1450,"date":"2017-06-15T22:28:54","date_gmt":"2017-06-16T05:28:54","guid":{"rendered":"http:\/\/www.elbeno.com\/blog\/?p=1450"},"modified":"2017-06-16T08:02:41","modified_gmt":"2017-06-16T15:02:41","slug":"c17-class-templates-deduced-or-not","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1450","title":{"rendered":"C++17 Class Templates: Deduced or Not?"},"content":{"rendered":"<p>C++17 introduces class template deduction: a way for the compiler to deduce the arguments to construct a class template without our having to write a <code>make_*<\/code> function. But it&#8217;s not quite as straightforward as it seems.<\/p>\n<p>Imagine we have a simple type that will tell us when it&#8217;s copied or moved, just for testing.<\/p>\n<pre lang=\"cpp\">\r\nstruct S\r\n{\r\n  S() = default;\r\n  S(const S&) { std::cout << \"copy\\n\"; }\r\n  S(S&#038;&#038;) { std::cout << \"move\\n\"; }\r\n};\r\n<\/pre>\n<p>And likewise a very simple template like so:<\/p>\n<pre lang=\"cpp\">\r\ntemplate <typename T>\r\nstruct Foo\r\n{\r\n  Foo(const T& t_) : t(t_) {}\r\n  Foo(T&& t_) : t(std::move(t_)) {}\r\n\r\nprivate:\r\n  T t;\r\n};\r\n<\/pre>\n<p>Note that we provide two forms of constructor to deal with both rvalues and lvalues being passed in. With C++14, prior to class template deduction, we would write a <code>make_foo<\/code> function to instantiate this template something like this:<\/p>\n<pre lang=\"cpp\">\r\ntemplate <typename T>\r\nauto make_foo(T&& t)\r\n{\r\n  return Foo<std::decay_t<T>>(std::forward<T>(t));\r\n}\r\n<\/pre>\n<p>And call it like so:<\/p>\n<pre lang=\"cpp\">\r\nint main()\r\n{\r\n  \/\/ rvalue: move\r\n  auto f1 = make_foo(S());\r\n\r\n  \/\/ lvalue: copy\r\n  S s;\r\n  auto f2 = make_foo(s);\r\n}\r\n<\/pre>\n<p>The important thing here is that the template argument to <code>make_foo<\/code> is deduced, and the template argument to <code>Foo<\/code> is not (cannot be, in C++14). Furthermore, because the template argument to <code>make_foo<\/code> is deduced, <code>make_foo<\/code>'s argument is a <em>forwarding reference<\/em> rather than an rvalue reference, and hence we use perfect forwarding to pass it along to the appropriate constructor for <code>Foo<\/code>.<\/p>\n<p>So far so good. Now, with C++17, class template deduction is available to us, so we can get rid of <code>make_foo<\/code>. Now our <code>main()<\/code> function looks like this:<\/p>\n<pre lang=\"cpp\">\r\nint main()\r\n{\r\n  \/\/ rvalue: move?\r\n  Foo f3{S()};\r\n\r\n  \/\/ lvalue: copy?\r\n  S s;\r\n  Foo f4{s};\r\n}\r\n<\/pre>\n<p>Here's the unintuitive part: the template arguments are being deduced now. But in that case, doesn't that mean that <code>Foo<\/code>'s constructor argument is being deduced? Which means that what looks like <code>Foo<\/code>'s rvalue constructor is actually now a constructor with a forwarding reference that will outcompete the other constructor? If so, that would be bad - we could end up <em>moving from an lvalue<\/em>!<\/p>\n<p>I woke up the other morning with this worrying thought and had to investigate. The good news is that even though the code looks like it would break, it doesn't.<\/p>\n<p>So in fact, yes, although <code>Foo<\/code>'s template argument is being deduced, I think the crucial thing is that <code>Foo<\/code>'s constructor still takes an rvalue reference - <em>not<\/em> a forwarding reference - at the point of declaration. And according to 16.3.1.8 [over.match.class.deduct] the compiler is forming an overload set of function templates that match the signatures of the constructors available, and it's using the correct types. In other words, I think it's doing something that we could not do: it's forming a function template, for the purposes of deduction, whose argument is an rvalue reference rather than a forwarding reference.<\/p>\n<p>As is often the case in C++, one needs to be careful to properly distinguish things. It is very easy to get confused over rvalue references and forwarding references, because they look the same. The difference is that forwarding references must be deduced... and in this case, even though it looks like it's deduced, it isn't.<\/p>\n<p>Edit: Reddit user mps1729 <a href=\"https:\/\/www.reddit.com\/r\/cpp\/comments\/6hkwd5\/c17_class_templates_deduced_or_not\/dizi95l\/\">points out<\/a> that indeed, neither of the implicitly generated functions is using a forwarding reference, as clarified in 17.8.2.1\/3 [temp.deduct.call]. Thanks for the clarification!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C++17 introduces class template deduction: a way for the compiler to deduce the arguments to construct a class template without our having to write a make_* function. But it&#8217;s not quite as straightforward as it seems. Imagine we have a simple type that will tell us when it&#8217;s copied or&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-1450","post","type-post","status-publish","format-standard","hentry","category-cpp"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1450"}],"version-history":[{"count":6,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1450\/revisions"}],"predecessor-version":[{"id":1456,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1450\/revisions\/1456"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}