{"id":110560,"date":"2024-11-22T07:00:00","date_gmt":"2024-11-22T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110560"},"modified":"2024-11-21T17:01:20","modified_gmt":"2024-11-22T01:01:20","slug":"20241122-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20241122-00\/?p=110560","title":{"rendered":"In C++, how can I make a default parameter be the this pointer of the caller?, revisited"},"content":{"rendered":"<p>Some time ago, we looked at <a title=\"In C++, how can I make a default parameter be the this pointer of the caller?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20231207-00\/?p=109115\"> making the default parameter of a method be the <code>this<\/code> pointer of the caller<\/a>. The scenario was something like this:<\/p>\n<pre>struct Property\r\n{\r\n    Property(char const* name, int initial, Object* owner) :\r\n        m_name(name), m_value(initial), m_owner(owner) {}\r\n\r\n    \u27e6 other methods elided - use your imagination \u27e7\r\n\r\n    char const* m_name;\r\n    Object* m_owner;\r\n    int m_value;\r\n};\r\n\r\nstruct Widget : Object\r\n{\r\n    Property Height{ \"Height\", 10, this };\r\n    Property Width{ \"Width\", 10, this };\r\n};\r\n<\/pre>\n<p>and we didn&#8217;t want to have to type <code>this<\/code> as the last parameter to all the <code>Property<\/code> constructors. We came up with this:<\/p>\n<pre>template&lt;typename D&gt;\r\nstruct PropertyHelper\r\n{\r\n    Property Prop(char const* name, int initial)\r\n    { return Property(name, initial, static_cast&lt;D*&gt;(this)); }\r\n};\r\n\r\nstruct Widget : Object, PropertyHelper&lt;Widget&gt;\r\n{\r\n    Property Height = Prop(\"Height\", 10);\r\n    Property Width = Prop(\"Width\", 10);\r\n};\r\n<\/pre>\n<p>Or, if you have access to <a title=\"C++23's Deducing this: what it is, why it is, how to use it\" href=\"https:\/\/devblogs.microsoft.com\/cppblog\/cpp23-deducing-this\/\"> deducing this<\/a>,<\/p>\n<pre>struct PropertyHelper\r\n{\r\n    template&lt;typename Parent&gt;\r\n    Property Prop(this Parent&amp;&amp; parent, char const* name, int initial)\r\n    { return Property(name, initial, &amp;parent); }\r\n};\r\n\r\nstruct Widget : Object, PropertyHelper\r\n{\r\n    Property Height = Prop(\"Height\", 10);\r\n    Property Width = Prop(\"Width\", 10);\r\n};\r\n<\/pre>\n<p>But this uses a fixed parameter list. What if you have to deal with multiple kinds of properties?<\/p>\n<pre><span style=\"border: solid 1px currentcolor;\">template&lt;typename T&gt;<\/span>\r\nstruct Property\r\n{\r\n    Property(char const* name, <span style=\"border: solid 1px currentcolor;\">T const&amp;<\/span> initial, Object* owner) :\r\n        m_name(name), m_value(initial), m_owner(owner) {}\r\n\r\n    \u27e6 other methods elided - use your imagination \u27e7\r\n\r\n    char const* m_name;\r\n    Object* m_owner;\r\n    <span style=\"border: solid 1px currentcolor;\">T<\/span> m_value;\r\n};\r\n<\/pre>\n<p>or arbitrary types, not just specializations of <code>Property<\/code>?<\/p>\n<pre>template&lt;typename Handler&gt;\r\nstruct Event\r\n{\r\n    Event(char const* name, Object* owner) :\r\n        m_name(name), m_owner(owner) {}\r\n\r\n    \u27e6 other methods elided - use your imagination \u27e7\r\n\r\n    char const* m_name;\r\n    Object* m_owner;\r\n    std::vector&lt;Handler&gt; m_handlers;\r\n};\r\n<\/pre>\n<p>What all of these little classes have in common is that they take a pointer to the containing class as the final parameter. Can we generalize this solution without having to create a bunch of one-off classes, one for each type we need to wrap?<\/p>\n<p>Sure. The idea is to use a trick we saw a little while ago: <a title=\"How can I have a C++ function that returns different types depending on what the caller wants?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191106-00\/?p=103066\"> You can simulate a function that returns different types depending on what the caller wants<\/a> by returning a proxy type that holds onto the parameters and then performs the work when the destination type is revealed by the conversion operator.<\/p>\n<pre>template&lt;typename...Args&gt;\r\nstruct maker\r\n{\r\n    std::tuple&lt;Args&amp;&amp;...&gt; m_args;\r\n\r\n    maker(Args&amp;&amp;... args) : m_args((Args&amp;&amp;)args...) {}\r\n\r\n    template&lt;typename T&gt;\r\n    operator T() {\r\n        return std::make_from_tuple&lt;T&gt;(std::move(m_args));\r\n    }\r\n};\r\n\r\ntemplate&lt;typename D&gt;\r\nstruct OwnerHelper\r\n{\r\n    template&lt;typename...Args&gt;\r\n    auto Owned(Args&amp;&amp;... args)\r\n    { return maker&lt;Args&amp;&amp;..., D*&gt;((Args&amp;&amp;)args..., static_cast&lt;D*&gt;(this)); }\r\n};\r\n\r\nstruct Widget : Object, OwnerHelper&lt;Widget&gt;\r\n{\r\n    Property&lt;int&gt; Height = Owned(\"Height\", 10);\r\n    Property&lt;std::string&gt; Name = Owned(\"Name\", \"\"s);\r\n    Event&lt;NameChangedHandler&gt; NameChanged = Owned(\"NameChanged\");\r\n};\r\n<\/pre>\n<p>There is a subtlety here: We need to cast <code>args<\/code> to ensure that its reference category is preserved.<\/p>\n<p>Note that the use of a parameter pack means that we have to write something like <code>\"\"s<\/code> or <code>std::string{}<\/code> to get an empty string, rather than the more convenient <code>{}<\/code> because <a title=\"Perfect forwarding forwards objects, not braced things that are trying to become objects\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230727-00\/?p=108494\"> template type parameters match types, and not braced things that could be used to construct a type but aren&#8217;t a type themselves<\/a>.<\/p>\n<p>As before, we can simplify this with <i>deducing this<\/i>:<\/p>\n<pre>struct OwnerHelper\r\n{\r\n    template&lt;<span style=\"border: solid 1px currentcolor;\">typename O,<\/span> typename...Args&gt;\r\n    auto Owned(<span style=\"border: solid 1px currentcolor;\">this O&amp;&amp; self<\/span>, Args&amp;&amp;... args)\r\n    {\r\n        return maker&lt;Args&amp;&amp;..., O*&gt;((Args&amp;&amp;)args..., <span style=\"border: solid 1px currentcolor;\">&amp;self<\/span>);\r\n    }\r\n};\r\n\r\nstruct Widget : Object, <span style=\"border: solid 1px currentcolor;\">OwnerHelper<\/span>\r\n{\r\n    Property&lt;int&gt; Height = Owned(\"Height\", 10);\r\n    Property&lt;std::string&gt; Name = Owned(\"Name\", \"\"s);\r\n    Event&lt;NameChangedHandler&gt; NameChanged = Owned(\"NameChanged\");\r\n};\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Expanding on the previous pattern.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-110560","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Expanding on the previous pattern.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110560","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=110560"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110560\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=110560"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110560"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110560"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}