在模板中执行参数依赖名称查找时的奇怪行为

sun*_*t07 5 c++ templates name-lookup

最近,我正在研究模板类中名称的众所周知的"两阶段名称查找"的确切含义.虽然我已经阅读了很多关于此的文章,但我仍然无法了解这一切.现在我对下面显示的代码感到困惑:

template<typename T>
class A
{
public:
    void f(T, T){};
};

namespace ns
{
    typedef int TT;
    void f(int, int){};
};

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

        //f(T(), T()); // it's fine for error here
        typedef ns::TT TTT;
        f(TTT(), T()); // why this issued an error?
        f(ns::TT(), T()); // and this?
    }
};

/* I also think it's OK to move ns here */
// namespace ns
// {
//  typedef int TT;
//  void f(int, int){};
//};

int main()
{
    B<int> b;
    b.g();
}
Run Code Online (Sandbox Code Playgroud)

请注意第二条评论.由于"f"是从属名称,因此应将其查找延迟到"main"函数中的实例化.此时,编译器应在main函数的范围内执行与参数相关的名称查找.我想现在它应该在命名空间ns中发现函数,但它仍然发出了编译错误:

1.cpp: In instantiation of 'void B<T>::g() [with T = int]':
1.cpp:30:6:   required from here
1.cpp:23:15: error: 'f' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]    f(TTT(), T()); //why this issued an error?
               ^
1.cpp:23:15: note: declarations in dependent base 'A<int>' are not found by unqualified lookup
1.cpp:23:15: note: use 'this->f' instead
Run Code Online (Sandbox Code Playgroud)

有人可以向我解释一下吗?谢谢.

dyp*_*dyp 3

参数相关查找仅搜索参数类型的关联类和命名空间。typedef 只是一个透明的别名,类似于 using 声明。

来自标准草案 n3485,[basic.lookup.argdep]/2 关于参数相关查找:

对于T函数调用中的每个参数类型,需要考虑一组零个或多个关联的命名空间以及一组零个或多个关联的类。命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)决定。用于指定类型的 Typedef 名称和 using 声明不参与此集合。

[强调我的]


template<typename T>
class A
{
public:
    void f(T, T){};
};

namespace ns
{
    typedef int TT;
    void f(int, int){};
};

template<typename T>
class B : public A<T> // note the base class is dependent
{
public:
    void g()
    {
        //f(T(), T()); // it's fine for error here
        typedef ns::TT TTT;
        f(TTT(), T()); // why this issued an error?
        f(ns::TT(), T()); // and this?
    }
};
Run Code Online (Sandbox Code Playgroud)

由于基类是依赖的,因此在非限定查找期间不会搜索它。因此,f可以使用参数相关查找来找到。您正确地指出,f仅在“第二阶段”(在实例化点)期间搜索,因为在您的调用中,至少一个参数依赖于模板参数。

但是,ns不是从属名称,以及TT(in ns::TT)。因此,命名空间 和TT必须在用于 的定义中使用之前进行声明g

无论您编写f(ns::TT(), T())f(T(), T())不影响f在参数相关查找期间搜索的一般规则:仅关联的命名空间和参数类型的类(ofTns::TT)。两者都是ints for B<int>::g(),因此没有关联的类和命名空间。

f(TTT(), TTT())更改查找,因为f现在是在名字查找阶段进行查找(它不是从属名称)。


下面是一个依赖于参数的查找的示例:

namespace ns
{
    struct TT {};
    void f(TT, TT) {}
}

int main()
{
    ns::TT x;
    f(x, x);
}
Run Code Online (Sandbox Code Playgroud)

现在,您也可以在类模板的成员函数中执行此操作:

namespace ns
{
    struct TT {};
    void f(TT, TT) {}
}

template<typename T>
struct B
{
    void g()
    {
        f(T(), T());
    }
};

int main()
{
    B<ns::TT> x;
    x.g();
}
Run Code Online (Sandbox Code Playgroud)

但是,正如我所说,依赖于参数的名称查找不适用于基本类型,例如int.

在上面的示例中,f又是依赖的,因为至少一个参数依赖于模板参数。因此,你也可以这样写:

template<typename T>
struct B
{
    void g()
    {
        f(T(), T()); // looked up during the second phase,
                     // from the point of instantiation
    }
};

namespace ns
{
    struct TT {};
    void f(TT, TT) {}
}

int main()
{
    B<ns::TT> x;
    x.g();
}
Run Code Online (Sandbox Code Playgroud)