2011-04-18 61 views
8

为什么在GCC 4.4中无法编译?在名称空间中重载函数模板

template<typename T> 
class A { 
public: 
    void foo() { 

    } 

private: 
    T x; 
}; 

namespace Ns { 
template<typename T> 
void do_it (A<T> a) { 
    a.foo(); 
} 
}; 

template<typename T> 
void myfun (T x) { 
    Ns::do_it (x); 
} 

template<typename T> 
class B { 
public: 
    void bar() { 

    } 

private: 
    T x; 
}; 

namespace Ns { 
template<typename T> 
void do_it (B<T> b) { 
    b.bar(); 
} 
}; 

int main() { 
    A<int> a; 
    B<int> b; 

    myfun (a); 
    myfun (b); // error: no matching function call to do_it(B<int>&) 

    return 0; 
} 

它必须与do_it的命名空间有关。当我删除它周围的命名空间时,它会编译。

背景:我正在构建一组可用于许多不同容器类的函数。为了统一处理不同的接口,我使用每个容器类都重载的独立函数。这些函数应放入一个名称空间以避免使用它们混淆全局名称空间。

B的定义应该被认为是来自不同的头文件而不是A的定义,所以重新排序不是一个选项。

+0

这是一个错字! HTML被吞噬了。 – 2011-04-18 17:37:44

+0

VS 2010编译上面的代码,我相信这样做是正确的,但这是一个棘手的例子。很好的问题! – 2011-04-18 18:49:53

+0

与VS2008一样,我只是检查它。这可能是GCC中的一个错误,或者它们的解释与微软的解释有什么不同?它没有名称空间的事实会指出它是一个错误,不是吗? – 2011-04-18 19:37:31

回答

6

原因是只有ADL在通话时完成。其他函数查找仅在myfun函数模板的定义中完成。

并且在该定义上下文中,仅声明do_it超载接受A<int>

编辑:如果您想为此设置标准参考,请参阅[temp.dep.candidate]和[temp.res] p1。

+0

有没有标准的方法来解决这个使用命名空间? – 2011-04-18 19:46:41

+2

@Mark如果想要考虑命名空间'Ns'中的函数,则可以从该名称空间中定义的虚拟类派生'B ',以便ADL查看'Ns'命名空间。或者,您可以将模板参数传递给'B ',将生成的专用化与'Ns'相关联。例如'B >(ADLAssociator可能只是一个只存储'T t;'的小类模板)。或者您将这些关联器类作为附加虚拟参数传递:'B '。这个有很多选择。最干净的似乎是从一个类派生'B'或者在'NS'中定义它。 – 2011-04-18 19:57:08

+5

如何将自由函数更改为静态类成员:'namespace Ns {template struct dispatch; }(声明前面),然后为每个新类型添加一个专门化:'namespace Ns {template struct dispatch < B> {static void do_it(B x){x.bar()}}; }',并有'template void myfunc(T t){Ns :: dispatch :: do_it(t); }' – 2011-04-18 20:12:55