警告,C ++讨厌...... 我在这里看到了这个问题略有不同的变化,这是我的看法
namespace space {
}
template <typename T> struct C
{
void foo() {
using namespace space;
bar(t);
}
T t;
};
class A {
};
namespace space {
void bar(const A& a){
}
}
int main()
{
C<A> c;
c.foo();
return 0;
}
如果bar不在命名空间内,则所有内容都可以正常编译。使用命名空间会破坏与gcc的编译。更有趣的是 - gcc找到了这个功能,但感觉不喜欢使用它:
ConsoleApplication1.cpp: In instantiation of 'void C<T>::foo() [with T = A]':
ConsoleApplication1.cpp:26:10: required from here
ConsoleApplication1.cpp:8:8: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point
instantiation [-fpermissive]
bar(t);
^
ConsoleApplication1.cpp:18:6: note: 'void space::bar(const A&)' declared here, later in the translation unit
void bar(const A& a){
这种行为是否有任何明智的理由,但标准是这样说的?是否有任何开关我可以让gcc接受这个代码,因为它似乎没有技术问题,我不明白为什么这样的代码不应该从纯语言的角度来运作。
答案 0 :(得分:4)
template
不是宏。符号的名称查找在两个上下文中完成:第一个,您编写template
的位置,以及第二个仅参数依赖查找(ADL或Koeing Lookup)的传递传递template
类型(当template
“工厂”被实例化为实际的class
或函数时)。
当函数与A
位于同一名称空间时,通过ADL找到它。如果不是,它就不会。因此,当您将bar
函数移动到space::bar
时,不再使用A
类型的参数找到ADL。
这是故意的,既可以阻止你的template
在不同的地方意味着完全不同的东西,也可以允许非成员界面扩展只在自己的命名空间中输入。
使用traits类,在类型相同的命名空间中粘贴这些辅助函数,或者传入函子。
特质课程可能最简单。创建一个具有静态函数foo
的类(如果需要默认实现:否则保留为空)。从您的template
拨打电话。专注于A
。现在,您可以在此时为A
类型实现特殊行为 - 如果该功能在视图中,它甚至可以调用您的space::bar
函数。
将bar
置于与A
相同的名称空间是另一种解决方案,我发现它比特征类更好。我的许多个人特征类最终都退回到ADL查找,因为这样我就可以在我定义A
的位置旁边注入处理代码。 (traits类的使用让我也可以在std
处理我的特征,我不允许为生活在std
中的类型注入ADL函数(你可以将ADL函数注入{{} 1}},但仅适用于您自己的类型))
仿函数解决方案是std
的工作原理 - 当它落在std::map
上时会回到std::less<T>
,它需要一个仿函数来指定密钥类型应该如何在此 operator<
内进行比较。
答案 1 :(得分:2)
这种行为是否有任何合理的理由,除了标准是这样说的?
或者不是,C ++通常需要在使用前声明名称。在使用之前,您不会声明bar
;具体而言,using namespace space
仅导入此时已声明的名称,而不是bar
。
(如果你关心它的原因,那是因为C ++从半个世纪前的语言中继承了它的声明规则,当时计算机有些原始。像这样的规则允许一次编译,所以编译器没有必须等待操作员将一堆穿孔卡放回料斗中。或类似的东西;我不太确定当时计算机是如何工作的。)
是否有任何开关我可以让gcc接受此代码
正如错误消息所示,-fpermissive
。但是,如果您不遵守标准,您的代码将无法移植。
答案 2 :(得分:0)
此错误与命名空间或模板完全无关,但是在声明之前使用函数的标准错误。正如你不能做的那样:
void caller() { callee(); }
void callee() {}
但必须改为:
void callee() {}
void caller() { callee(); }
或者:
void callee();
void caller() { callee(); }
void callee() {}
......这同样适用于涉及模板和命名空间的更复杂的功能。请注意,如果您稍微重新排序,它可以正常工作:
class A {
};
namespace space {
void bar(const A& a){
}
}
template <typename T> struct C
{
void foo() {
using namespace space;
bar(t);
}
T t;
};
int main()
{
C<A> c;
c.foo();
return 0;
}