{"id":111807,"date":"2025-11-21T07:00:00","date_gmt":"2025-11-21T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111807"},"modified":"2025-11-21T13:45:39","modified_gmt":"2025-11-21T21:45:39","slug":"20251121-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251121-00\/?p=111807","title":{"rendered":"Maybe somebody can explain to me how weak references solve the ODR problem"},"content":{"rendered":"<p>Some time ago, I proposed <a title=\"Using type aliasing to avoid the ODR problem with conditional compilation, part 2\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250502-00\/?p=111140\"> Using type aliasing to avoid the ODR problem with conditional compilation<\/a>. A Reddit thread claimed that &#8220;<a href=\"https:\/\/reddit.com\/r\/cpp\/comments\/1ke9r86\/using_type_aliasing_to_avoid_the_odr_problem_with\/mqhba32\/\">It&#8217;s weak functions basically. You can actually achieve this without needing templates but need to separate the weak and non-weak definitions<\/a>.&#8221;<\/p>\n<p>I&#8217;m not seeing how weak functions help here, but maybe somebody can set me straight.<\/p>\n<p>Weak functions are still functions. If you are changing data layout (as we are in our case of adding a <code>Logger<\/code> member), you can weaken any functions that access the data members, but that doesn&#8217;t help any class consumers that access those data members, because the data member accesses are inlined into the call site.<\/p>\n<p>Let&#8217;s look at our <code>Widget<\/code> again.<\/p>\n<pre>\/\/ widget.h\r\n\r\nstruct Widget\r\n{\r\n    Widget();\r\n\r\n    void SetName(std::string const&amp; name);\r\n\r\n    \u27e6 more stuff \u27e7\r\n\r\n#ifdef EXTRA_WIDGET_DEBUGGING\r\n    Logger m_logger;\r\n\r\n    void Log(std::string const&amp; message) {\r\n        m_logger.log(message);\r\n    }\r\n#else\r\n    void Log(std::string const&amp; message) {\r\n        \/\/ no extra logging\r\n    }\r\n#endif\r\n\r\n    std::string m_name;\r\n};\r\n<\/pre>\n<p>If somebody just accesses the name directly:<\/p>\n<pre>void sample(Widget&amp; w)\r\n{\r\n    std::cout &lt;&lt; w.m_name;\r\n}\r\n<\/pre>\n<p>The access to <code>w.m_name<\/code> is inlined into the <code>sample<\/code> function. It&#8217;ll probably go something like<\/p>\n<pre>    mov     rsi, edi                ; rsi -&gt; Widget\r\n#ifdef EXTRA_WIDGET_DEBUGGING\r\n    add     rsi, 16                 ; offset of m_name\r\n                                    ; if there is a Logger\r\n#else\r\n    add     rsi, 24                 ; offset of m_name\r\n                                    ; if there isn't a Logger\r\n#endif\r\n    mov     rdi, offset std::cout\r\n    call    operator&lt;&lt;        ; print the name to cout\r\n<\/pre>\n<p>The compiler is going to inline the offset to <code>m_name<\/code>, so it will choose an offset based on whether the <code>EXTRA_<wbr \/>WIDGET_<wbr \/>DEBUGGING<\/code> symbol is set. But if the caller is building with <code>EXTRA_<wbr \/>WIDGET_<wbr \/>DEBUGGING<\/code> set the opposite way, then you are printing garbage.<\/p>\n<p>The idea behind templating the <code>Widget<\/code> based on whether it is debugging or not is to allow parts of the code to use a debugging Widget, but other parts to use a non-debugging Widget.<\/p>\n<pre>template&lt;bool debugging&gt;\r\nstruct WidgetT\r\n{\r\n    \u27e6 ... \u27e7\r\n};\r\n\r\n#ifdef EXTRA_WIDGET_DEBUGGING\r\nusing Widget = WidgetT&lt;true&gt;;\r\n#else\r\nusing Widget = WidgetT&lt;false&gt;;\r\n#endif\r\n<\/pre>\n<p>The pieces that use a debugging widget get <code>WidgetT&lt;true&gt;<\/code>, and those that don&#8217;t get a <code>WidgetT&lt;false&gt;<\/code>.<\/p>\n<p>While weak pointers let you provide two versions of a function (say, debugging and non-debugging), you can&#8217;t have both versions active at the same time. Everybody has to agree on whether it&#8217;s a debugging version or a non-debugging version.<\/p>\n<p>But maybe I&#8217;m missing something. If you can explain how to accomplish this with weak functions, let me know in the comments.<\/p>\n<p><b>Bonus chatter<\/b>: Another downside of weak functions is that they aren&#8217;t inlined, but maybe link time optimization can re-inline them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I don&#8217;t see it.<\/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-111807","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>I don&#8217;t see it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111807","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=111807"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111807\/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=111807"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111807"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111807"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}