朋友模板参数依赖查找

时间:2015-01-13 08:22:06

标签: c++ templates visual-c++ friend argument-dependent-lookup

我们知道在类范围中定义的友元函数可以通过参数依赖查找找到,所以我们必须在友元函数类型中使用类类型,但是如果我们在类之外定义友元函数,它的函数参数可以是留空了。 那么这对于模板朋友是如何工作的,如果我们在类之外有专门化,它应该是在类范围之外定义的普通友元函数可见。

#include <iostream>    
class A
{
public:
        A()
         : x(20)
        {}
        template <typename T>
        friend void foo()
        {
               std::cout << "Primary Template" << std::endl;
        }
        friend void goo();
private:
        int x;
};

void goo() 
{
    std::cout << "some goo" << std::endl;
}


template <>
void foo<int>()
{
        std::cout << "specialization" << std::endl;
}

int main()
{
        A a;
        foo<int>(); //  VS 2012 gives error C3767: 'foo': candidate function(s) 
                    // not accessible
                    //  'foo'  [may be found via argument-dependent lookup]
        goo();      // OK
}

那么为什么goo是可见的和可访问的但是foo的专业化不是int? VisualStudio 2012提供错误&#34;&#39; foo&#39;:候选功能无法访问,&#39; foo&#39; [可以通过参数依赖查找找到]&#34;。顺便说一句,GCC编译代码没有错误。标准中是否有任何限制,这只是编译器问题?

1 个答案:

答案 0 :(得分:3)

我认为Clang在这里可能是正确的。考虑两件事:

§14.7.3/ 8 [temp.expl.spec],稍微贬低了这个例子:

  

模板显式特化是在命名空间的范围内   其中定义了模板。 [实施例

namespace N {
    template<class T> class X { /* ... */ };
    template<> class X<int> { /* ... */ }; // OK: specialization 
                                           // in same namespace
}
template <> class N::X<double> { /* ... */ }; // OK: specialization
                                              // in enclosing namespace
     

- 示例]

但是,根据§11.3/ 7 [class.friend](强调我的),所讨论的朋友函数foo存在:

  

在类中定义的友元函数在(词法)范围中   定义它的类。

要提供foo的专业化,它必须在A的词法范围内 - 我认为不可能这样做。您的foo<int>专业化范围错误。

请注意,这仅适用于在类中定义函数的情况。以下为Clang编译并运行,因为现在goo不在M的范围内:

#include <iostream>
struct M
{
    template <typename T>  
    friend void goo();     // only declared, not defined
};

template <typename T>
void goo() { 
    std::cout << "full" << std::endl;
}

template <>
void goo<int>() {
    std::cout << "explicit" << std::endl;
}

int main()
{
    goo<int>(); // prints explicit
}

可见性规则在§7.3.1.2/ 3(强调我的)中确定:

  

friend声明 本身使名称可见   非限定查找(3.4.1)或限定查找(3.4.3)。 [注意:朋友的名字将在其名称空间中显示如果在命名空间范围内提供匹配声明(在授予友谊的类定义之前或之后)。 -end note ]

因此,在这个更简单的例子中:

struct M {
    template <typename T> friend void foo(T ) { }
};

fooM中定义,因此它存在于M的词法范围内。在foo之外没有M的“匹配声明”,所以它只能在ADL中显示(§3.4.2/ 4,强调我的):

  

在考虑关联的命名空间时,查找与执行时的查找相同   关联命名空间用作限定符(3.4.3.2),但

除外      

(4.1) - 忽略相关命名空间中的任何 using-directives

     

(4.2) - 在相关的类中声明的任何命名空间范围的朋友函数或朋友函数模板在其各自的命名空间中是可见的 即使它们在普通的期间不可见查找(11.3)。

int main() {
    foo(M{}); // compiles correctly on both GCC and Clang
    foo(0);   // should fail. Clang complains about "use of undeclared identifier 'foo'"
              // but GCC 4.9.2 allows it! (update: fixed in 5+)
}

所以我重申我的第一句话:我认为Clang在这里可能是正确的。