首页 > 解决方案 > 为什么(或何时)模板引入其命名空间?

问题描述

这是一个例子:

#define MAKE_IT_WORK false
namespace Bob {  // Bob's project namespace
   struct DeviceFrequency {};
   extern void debugf(const char* fmt, ...);
}  // namespace Bob

namespace SSN {  // Super-Secret Namespace
   namespace debugging {
     extern int  ssn_debug;
     extern void debugf(const char* fmt, ...);
   }  // namespace debugging
}  // namespace SSN

namespace SSN::signals { // Super-Secret Namespace, project signals
   template<typename Coder> // In the example, this imports Bob's namespace
   class Frequency {
     public:
     Frequency( void )
     { using namespace ::SSN::debugging; // Why isn't this enough??
       using ::SSN::debugging::debugf;   // Or this??
       if( ssn_debug )
       #if MAKE_IT_WORK
         ::SSN::debugging::  // How can a developer predict that this is needed??
       #endif
         debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
     }
   }; // class Frequency
}  // namespace SSN::signals

struct Controller {
   SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
   Controller( void ) : bobcon() {}
}; // class Controller

在此示例中,Bob 复制了该debugf函数,因为他不想将整个 SSN 名称空间带入他的私有名称空间,也不想在每次使用它时都为完全限定它而烦恼。

SSN 开发人员没有意识到模板也可以导入其名称空间(直到它发生),显然使 ADL 查找发挥了作用。尽管 ADL 查找声明命名空间中的 using 语句被忽略,但它为什么发挥作用根本没有意义。一个命名空间无意中污染另一个命名空间似乎太容易了,而且很难预测有朝一日这可能会发生在内联代码中的什么地方。

看起来(至少)每当在命名空间中使用模板时,每个命名空间函数引用都必须是完全限定的,因为在使用模板时,您无法预测何时可能会发生名称冲突。它是否正确?如果是这样,有什么方法可以避免所有似乎需要的额外名称限定符输入?这种暴露是否仅限于模板,还是所有导入的内联代码都以某种方式同样容易受到攻击?

使用 gcc 版本 10.2.0 和 10.2.1 (Red Hat 10.2.1-5) 编译 (Dirty.cpp),我收到以下消息:

make dirty 
c++  -o Dirty.o -c S/Dirty.cpp  -D_CC_GCC -D_OS_BSD -D_HW_X86 -D_OS_LINUX -IS -IH -g -O3 -finline->functions -std=gnu++17 -Wall -Wextra -Wmissing-declarations -Wswitch-default -Werror
S/Dirty.cpp: In instantiation of ‘SSN::signals::Frequency<Coder>::Frequency() [with Coder = Bob::DeviceFrequency]’:
S/Dirty.cpp:146:32:   required from here
S/Dirty.cpp:139:12: error: call of overloaded ‘debugf(const char [30], SSN::signals::Frequency<Bob::DeviceFrequency>*, long unsigned int)’ is ambiguous
 139 |      debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
     |      ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
S/Dirty.cpp:124:13: note: candidate: ‘void SSN::debugging::debugf(const char*, ...)’
 124 | extern void debugf(const char* fmt, ...);
     |             ^~~~~~
S/Dirty.cpp:117:13: note: candidate: ‘void Bob::debugf(const char*, ...)’
 117 | extern void debugf(const char* fmt, ...);
     |

(已编辑:该示例已恢复为原始示例,现在由@1201ProgramAlarm 重新格式化。出现了一个修改版本,测试示例如下面的部分答案。)

标签: c++templatesnamespacesc++17

解决方案


对于模板,ADL 包括与为模板类型参数提供的模板参数类型相关的命名空间和实体。因为调用debugf包含this作为参数,所以 ADL 将包含namespace Bob,因为模板是用Bob::DeviceFrequency.


推荐阅读