为啥结构必须与模板类在同一个命名空间中才能编译?
Posted
技术标签:
【中文标题】为啥结构必须与模板类在同一个命名空间中才能编译?【英文标题】:Why does a struct have to be in the same namespace as a template class in order to compile?为什么结构必须与模板类在同一个命名空间中才能编译? 【发布时间】:2014-12-30 19:24:53 【问题描述】:问题的标题并没有透露太多关于我的问题,但我试图用一个短语来解释这个问题。这是问题所在,我在使用 Windows 中的 MinGW 和 Linux 中的 GCC 编译的应用程序中具有类似的代码结构。 Visual Studio 没有任何问题。结构如下:
#include <iostream>
namespace idb
class Wrapper
public:
template<typename S>
void boo(S& s)
bind(s);
;
namespace idb // <- if this namespace changes, code explodes
struct Fulalas
int x;
;
namespace idb
void bind(idb::Fulalas f)
std::cout << f.x << std::endl;
namespace app
class Foo
public:
void func()
idb::Fulalas f;
f.x = 5;
w.boo(f);
private:
idb::Wrapper w;
;
int main()
app::Foo f;
f.func();
return 0;
问题是为什么在 GCC/MinGW 中将 idb::Fulalas
更改为 aaa::Fulalas
(或任何所需的名称)会产生以下错误:
..\namespace\main.cpp: In instantiation of 'void idb::Wrapper::boo(S&) [with S = aaa::Fulalas]':
..\namespace\main.cpp:41:11: required from here
..\namespace\main.cpp:11:10: error: 'bind' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
bind(s);
^
..\namespace\main.cpp:26:7: note: 'void idb::bind(aaa::Fulalas)' declared here, later in the translation unit
void bind(aaa::Fulalas f)
【问题讨论】:
这不是您的代码,也不是产生此错误消息的代码。请复制并粘贴一个有代表性的测试用例。 如果不想利用ADL,可以将aaa::Fulalas
和idb::bind
的声明移到调用idb::bind
之前的地方
这个问题是关于C++的,所以应该去掉C标签
@Columbo “这不是你的代码”是什么意思?当然是我的代码,我写的。
【参考方案1】:
这与结构无关。
这是关于 bind
和参数依赖查找。
bind
使用不合格,因此在相关的命名空间中寻找它。如果 struct
与绑定重载不在同一个命名空间中,则不会看到/考虑它。
【讨论】:
你回答了一个与 Boost 无关的问题?! 这与 boost 无关。 Bind 只是我给出的一个示例函数名称。 非常好的和简单的答案@sehe。你知道为什么这在 Visual Studio 2013 中可以正常工作吗? @McLeary 是的。 MSVC 是一个在两阶段查找实现方面出了名的损坏编译器(这意味着在模板附近的任何地方,这是......总是,你可以依赖名称查找是不稳定的)。大多数情况下,根据标准,您获得的查找候选者比“合法”应有的“更多”,因此这通常不是问题,但您可能会遇到可移植性问题。 @sehe:我个人认为 MSVC 的模型比 2-phase 更有意义,所以我不会将其描述为“古怪”,但肯定是不合格的。【参考方案2】:在表达式中
bind(s)
名称bind
是依赖的,因为参数s
的类型为S
,它是一个模板参数。从属名称在模板实例化时绑定。当您调用时会发生这种情况
w.boo(f);
并因此将boo
实例化为S = idb::Fulalas
类型。解析依赖名称时,会考虑来自两个来源的声明:
在模板定义处可见的声明。 来自与函数参数类型关联的命名空间的声明 实例化上下文 (14.6.4.1) 和定义上下文。
([temp.dep.res])
因此,如果在模板的定义点之后但在实例化点之前声明了一个名称,则只能通过 ADL 找到它,而不能通过普通的非限定名称查找。普通的非限定查找只能找到在模板定义点可见的名称,而不是在实例化点。因此,如果Fulalas
没有声明在与bind
相同的命名空间中,ADL 将找不到idb::bind
的声明,名称查找将完全失败。
【讨论】:
以上是关于为啥结构必须与模板类在同一个命名空间中才能编译?的主要内容,如果未能解决你的问题,请参考以下文章