gcc模板分辨率和命名空间

时间:2014-05-02 10:50:38

标签: c++ templates gcc namespaces

警告,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接受这个代码,因为它似乎没有技术问题,我不明白为什么这样的代码不应该从纯语言的角度来运作。

3 个答案:

答案 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;
 }