【问题标题】:ADL and friend injectionADL 和朋友注入
【发布时间】:2010-09-02 22:12:22
【问题描述】:

考虑这段代码:

template <int N>
struct X
{
 friend void f(X *) {}
};

int main()
{
 f((X<0> *)0); // Error?
}

编译器似乎非常不同意。 (MSVC08/10 说不,GCC

根据“C++ 模板 - 完整指南”:

...假设调用 涉及查找朋友 关联的类实际上导致 要实例化的类......虽然 这显然是那些 写了 C++ 标准,它不是 标准中有明确规定。

我在标准中找不到相关部分。有参考吗?

考虑这种变化:

template <int N>
struct X
{
 template <int M>
 friend void f(X<M> *) {}
};

template <>
struct X<0>
{
};

int main()
{
 X<1>();
 f((X<0> *)0); // Error?
}

这里的关键问题是X&lt;1&gt; 注入的可行函数在X&lt;0&gt; 的ADL 期间是否应该可见?它们有关联吗?上面提到的所有编译器都接受这个代码,除了 Comeau,它只在宽松模式下接受它。也不确定标准对此有何规定。

您对此有何看法?

【问题讨论】:

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


    【解决方案1】:

    标准说14.7.1/4

    如果在需要完全定义的对象类型的上下文中使用类类型,或者如果类类型的完整性影响程序的语义,则类模板特化会被隐式实例化;特别是,如果类型为类模板特化的表达式涉及重载决议、指针转换、指向成员的转换,则类模板特化被隐式实例化(3.2);

    请注意,Vandervoorde 发了issue report here,委员会发现

    标准已经规定这会创建一个实例化点。

    对于第二种情况 - 您需要考虑参数 f(X&lt;0&gt;*) 的关联类和命名空间。这些是,因为这是一个指向类模板专业化的指针(请注意,下面的“template-id”并不完全正确 - C++0x 更正了它以使用正确的术语),也是一个指向类的指针(这个令人困惑的拆分在 C++0x 中也进行了更正 - 它在一个要点中列出了这两种情况)。

    • 如果 T 是模板 ID,则其关联的命名空间和类是模板所在的命名空间 定义; [...很多噪音...]

    • 如果 T 是类类型(包括联合),则其关联的类是:类本身;它所属的类别(如有的话);及其直接和间接基类。其关联的命名空间是定义其关联类的命名空间。

    总而言之,我们有关联的类是X&lt;0&gt;,关联的命名空间是全局命名空间。现在可见的友元函数是

    • 在关联类中声明的任何命名空间范围的友元函数在它们各自的命名空间中都是可见的,即使它们在普通查找期间不可见

    X&lt;0&gt; 中没有声明友元函数,因此在查看全局命名空间时,友元函数声明不可见。请注意,X&lt;0&gt; 是与X&lt;1&gt; 完全不同的类类型。 X&lt;1&gt; 的隐式实例化对此调用没有影响 - 它只是将一个不可见的名称添加到全局命名空间中,该名称引用类 X&lt;1&gt; 的友元函数。

    【讨论】:

    • 那么第一个例子应该可以编译吗?
    • 谢谢 :) 这或多或少是我所怀疑的。为什么这么多编译器会弄错第二种情况?另外,如果您也可以参考其他报价的标准,我会很高兴。
    • @uj2 好吧,至少与 GCC 相比,clang 看起来不错。它正确地拒绝了第二种情况。特别是 GCC 有相当多的模板一致性问题,所以这并不让我感到惊讶。您正在尝试查找标准段落的其他引用是什么?
    • @jpalecek 是的,第一个示例应该编译。
    • @litb: clang 失败了第一个,也找不到自己是模板的朋友......我指的是最后两个引号:“如果 T 是模板 ID .. ." 和 "任何命名空间范围的朋友..."
    猜你喜欢
    • 1970-01-01
    • 2011-08-10
    • 1970-01-01
    • 2015-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-15
    相关资源
    最近更新 更多