Learning to read C++ compiler errors: Ambiguous symbol errors after including a header file

Finding out why multiple entities with the same name are visible. The post Learning to read C++ compiler errors: Ambiguous symbol errors after including a header file appeared first on The Old New Thing.

Jun 19, 2025 - 23:40
 0
Learning to read C++ compiler errors: Ambiguous symbol errors after including a header file

A colleague added another header file to their project, and everything started blowing up with “ambiguous symbol” errors.

#include "pch.h"
#include "something.h"
#include "newheader.h"
#include 

...

This resulted in a build error in Visual Studio:

D:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um\shlobj_core.h(236,1): error C2872: 'IUnknown': ambiguous symbol
(compiling source file 'Widget.cpp')
    D:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um\unknwnbase.h(117,9):
        could be 'IUnknown'
    D:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\cppwinrt\winrt\base.h(312,12):
    or       'winrt::Windows::Foundation::IUnknown'

(repeat a gazillion more times)

The compiler says that the problem is with shlobj_core.h, but really, that’s just where the problem was discovered.

The offending line in shlobj_core.h is

DECLARE_INTERFACE_IID_(IExtractIconA, IUnknown, "000214eb-0000-0000-c000-000000000046")

The DECLARE_INTERFACE_IID_ macro expands to

struct __declspec(uuid(000214eb-0000-0000-c000-000000000046"))
      __declspec(novtable) IExtractIconA : public IUnknown

The compiler reports a problem with the name IUnknown that is being used as the base class because it is ambiguous. It could refer to IUnknown (in the global namespace) or winrt::Windows::Foundation::IUnknown.

But wait, how could IUnknown, when referenced from the global namespace, end up referring to a name in namespace scope?

Answer: If the name has been imported into the global namespace via a using directive.¹

After some searching, they found a header file that contained the line

using namespace winrt::Windows::Foundation;

This imports all of winrt::Windows::Foundation into the global namespace, and that’s creating the name collision.

Putting using namespace directives in header files goes against SF.7: Don’t write using namespace at global scope in a header file.

Instead, qualify the names in the header file. Yes, this makes things wordy, but it’s better than polluting the global namespace.

Importing names into the global namespace should either be scoped or performed from the main C++ file rather than any headers.

¹ Another possible answer is “If the namespace has been added to the search via argument-dependent lookup.” However, this particular usage is not as a function call, so argument-dependent lookup does not apply, seeing as there are no arguments.

The post Learning to read C++ compiler errors: Ambiguous symbol errors after including a header file appeared first on The Old New Thing.