5

最近我正在研究著名的“两阶段名称查找”对模板类中名称的确切含义。尽管我已经阅读了很多关于这方面的文章,但我仍然无法了解这方面的一切。现在我对下面显示的代码感到困惑:

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();
}

请注意第二条评论。由于“f”是一个依赖名称,它的查找应该延迟到“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

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

4

1 回答 1

3

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

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

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

[强调我的]


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?
    }
};

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

但是,不是ns从属名称,以及(in )。因此,命名空间 和必须在它们用于定义.TTns::TTTTg

您是否编写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);
}

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

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();
}

但是,正如我所说,依赖于参数的名称查找不适用于基本类型,例如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();
}
于 2014-01-04T18:32:59.633 回答