{"id":112118,"date":"2026-03-09T07:00:00","date_gmt":"2026-03-09T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=112118"},"modified":"2026-03-09T10:47:41","modified_gmt":"2026-03-09T17:47:41","slug":"20260309-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20260309-00\/?p=112118","title":{"rendered":"Learning to read C++ compiler errors: Ambiguous overloaded operator"},"content":{"rendered":"<p>A customer was adding a feature to an old C++ code base, and the most convenient way to consume the feature was to use C++\/WinRT. However, once they added C++\/WinRT to their project, they ran into compiler errors in parts of their code that hadn&#8217;t changed in decades. As an added wrinkle, the problem occurred only in 32-bit builds.<\/p>\n<pre>std::ostream&amp; operator&lt;&lt;(ostream&amp; os, LARGE_INTEGER const&amp; value)\r\n{\r\n    return os &lt;&lt; value.QuadPart; \/\/ \u2190 error\r\n}\r\n<\/pre>\n<p>The error complained that the <code>&lt;&lt;<\/code> operator was ambiguous.<\/p>\n<pre style=\"white-space: pre-wrap;\">contoso.cpp(3141) : error C2593: 'operator &lt;&lt;' is ambiguous\r\nostream(436): note: could be 'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(long double)'\r\nostream(418): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(double)'\r\nostream(400): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(float)'\r\nostream(382): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(unsigned __int64)'\r\nostream(364): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(__int64)'\r\nostream(346): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(unsigned long)'\r\nostream(328): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(long)'\r\nostream(309): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(unsigned int)'\r\nostream(283): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(int)'\r\nostream(264): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(unsigned short)'\r\nostream(230): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(short)'\r\nostream(212): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt;::operator &lt;&lt;(bool)'\r\ncontoso.h(1554): note: or       'std::ostream &amp;operator &lt;&lt;(std::ostream &amp;,const unsigned __int64 &amp;)'\r\ncontoso.h(1548): note: or       'std::ostream &amp;operator &lt;&lt;(std::ostream &amp;,const __int64 &amp;)'\r\nostream(953): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::operator &lt;&lt;&lt;std::char_traits&lt;char&gt;&gt;(std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;,unsigned char)'\r\nostream(942): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::operator &lt;&lt;&lt;std::char_traits&lt;char&gt;&gt;(std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;,signed char)'\r\nostream(819): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::operator &lt;&lt;&lt;std::char_traits&lt;char&gt;&gt;(std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;,char)'\r\nostream(738): note: or       'std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;std::operator &lt;&lt;&lt;char,std::char_traits&lt;char&gt;&gt;(std::basic_ostream&lt;char,std::char_traits&lt;char&gt;&gt; &amp;,char)'\r\ncontoso.cpp(1582): note: while trying to match the argument list '(std::ostream, LONGLONG)'\r\n<\/pre>\n<p>All of these are different overloads of <code>std::ostream&amp; std::ostream::operator&lt;&lt;(something)<\/code> or <code>std::ostream&amp; operator&lt;&lt;(std::ostream&amp;, something)<\/code>, so let&#8217;s remove all the repeated stuff to make it easier to see what we are up against.<\/p>\n<pre style=\"white-space: pre-wrap;\">ostream(436): long double\r\nostream(418): double\r\nostream(400): float\r\nostream(382): unsigned __int64\r\nostream(364): __int64\r\nostream(346): unsigned long\r\nostream(328): long\r\nostream(309): unsigned int\r\nostream(283): int\r\nostream(264): unsigned short\r\nostream(230): short\r\nostream(212): bool\r\ncontoso.h(1554): const unsigned __int64 &amp;\r\ncontoso.h(1548): const __int64 &amp;\r\nostream(953): unsigned char\r\nostream(942): signed char\r\nostream(819): char\r\nostream(738): char\r\n<\/pre>\n<p>From the code, we see that the intention is to use the insertion operator that takes a signed 64-bit integer, so let&#8217;s filter down to those.<\/p>\n<pre style=\"white-space: pre-wrap;\">ostream(364): __int64\r\ncontoso.h(1548): const __int64 &amp;\r\n<\/pre>\n<p>Aha, now we see the conflict. The C++ standard library (<code>&lt;ostream&gt;<\/code>) has defined an output inserter for <code>__int64<\/code>, and the customer has defined an output inserter for <code>const __int64&amp;<\/code>, so the compiler can&#8217;t choose between them.<\/p>\n<p>The compiler kindly provided line numbers, so we can look at the conflict introduced by <code>contoso.h<\/code>.<\/p>\n<pre>#if !defined(_WIN64) &amp;&amp; !defined(_STL70_) &amp;&amp; !defined(_STL110_)\r\n\r\n\/\/ These are already defined in STL\r\nstd::ostream&amp; operator&lt;&lt;(std::ostream&amp;, const __int64&amp; );\r\nstd::ostream&amp; operator&lt;&lt;(ostd::stream&amp;, const unsigned __int64&amp; );\r\n\r\n#endif \/* _WIN64 _STL70_ _STL110_ *\/\r\n<\/pre>\n<p>Okay, well the <code>!defined(_WIN64)<\/code> explains why this problem occurs only in 32-bit builds: The conflicting definition is <code>#if<\/code>&#8216;d out in 64-bit builds.<\/p>\n<p>The rest of the <code>#if<\/code> expression removes the conflicting definition for STL versions 7.0 and 11.0. So what happened to reactivate this code path?<\/p>\n<p>Adding C++\/WinRT to the project.<\/p>\n<p>C++\/WinRT requires C++17 or later, which means that the project had to bump its compiler version, and that pushed the STL version to 12.0. And their custom <code>#if<\/code> doesn&#8217;t handle that case.<\/p>\n<p>I went back through the project history, and saw that about five years ago, the line was just<\/p>\n<pre>#if !defined(_WIN64) &amp;&amp; !defined(_STL70_)\r\n<\/pre>\n<p>So I&#8217;m guessing that at some point in the past five years, they upgraded their compiler version, and they ran into this <i>exact problem<\/i>, and they realized, &#8220;Oh, we need to suppress this for STL 11.0, too,&#8221; and they added the <code>!defined(_STL110_)<\/code>.<\/p>\n<p>History repeats itself.<\/p>\n<p>One solution is to put another layer of duct tape on it.<\/p>\n<pre>#if !defined(_WIN64) &amp;&amp; !defined(_STL70_) &amp;&amp; !defined(_STL110_) <span style=\"border: solid 1px currentcolor;\">&amp;&amp; !defined(_STL120_)<\/span>\r\n<\/pre>\n<p>Of course, this just means that in another five years, when they decide to upgrade to C++30, this problem will come back and somebody will have to add yet another layer of duct tape.<\/p>\n<p>So they could choose something that is a bit more forward-compatible:<\/p>\n<pre>#if !defined(_WIN64) &amp;&amp; !defined(_STL70_) &amp;&amp; !defined(_STL110_) <span style=\"border: solid 1px currentcolor;\">&amp;&amp; __cplusplus &lt; 201700<\/span>\r\n<\/pre>\n<p>Or they could just delete the entire block. I doubt they are going to roll their compiler back to C++03.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Look for the conflicting definitions to see where they are coming from.<\/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-112118","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Look for the conflicting definitions to see where they are coming from.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112118","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=112118"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/112118\/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=112118"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=112118"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=112118"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}