【问题标题】:Strange behavior when perform argument dependent name lookup in template在模板中执行参数相关名称查找时的奇怪行为
【发布时间】:2014-01-04 18:00:03
【问题描述】:

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

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

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

【问题讨论】:

  • [basic.lookup.argdep]/2 "对于函数调用中的每个参数类型T,都有一组零个或多个关联的命名空间和一组零个或多个关联的类可以考虑。命名空间和类的集合完全由函数参数的类型(以及任何模板模板参数的命名空间)决定。用于指定类型的 Typedef 名称和 using-declarations 对这个集合没有贡献." [强调我的] 我认为这个问题与模板无关。
  • 那么第三条评论出现的错误呢? f(ns::TT(), T())?
  • 那么ns::TTint 的类型定义。而ns 不是int 的关联命名空间。同样的规则也适用。 (ns:: 的存在只会更改 :: 之后的 TT 的名称查找,它不会更改 f 的名称查找 - 这仍然仅取决于参数的类型。)
  • 啊 - 等等,我不知何故忽略了继承。在非限定查找期间不搜索基类范围,因为基类A&lt;T&gt; 是依赖的。
  • @DyP,好的,谢谢!那么.. 此外,您能否向我展示一个在类似情况下参数依赖名称查找生效的示例?

标签: c++ templates name-lookup


【解决方案1】:

依赖于参数的查找仅搜索参数类型的关联类和命名空间。 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 只会在“第二阶段”(在实例化点)被搜索,因为在您的调用中,至少一个参数取决于模板参数。

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

无论您写f(ns::TT(), T()) 还是f(T(), T()) 都不会影响在依赖于参数的查找期间搜索f 的一般规则:仅关联的命名空间和参数类型的类(T 和@ 987654335@)。两者都是ints for B&lt;int&gt;::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();
}

【讨论】:

  • 感谢您的解释!还有一个问题,如果我将命名空间 ns 分成两部分:struct TT 定义在 main 函数之前,f 定义在 main 函数之后。不会发生错误。这是正确的吗?如果我添加“ns::TT y; f(y, y);”在主函数中的 x.g() 并移动声明之后,它说“未声明 f”?但是对于模板就可以了吗?
  • @sunlight07 这就是在模板内查找从属名称和在模板外查找名称之间的区别:f in f(T(), T()) 是一个从属名称,并且从“在第二阶段”查找实例化。实际上,有几个这样的点,一个在文件的末尾。因此,可以在g 的定义之后甚至在main 中调用g 之后找到f 的声明。但是,IIRC 你需要小心(调用后的声明)。
  • 作为一个实际问题(奇怪的是没有人提到这一点),我认为本地 using ns::f; 会创造奇迹。
  • @DyP er...我很困惑..我不太了解“几个这样的点”,尤其是文件末尾的那个。在 gcc 更改日志gcc.gnu.org/gcc-4.7/changes.html 中,有两个关于两阶段名称查找的示例。我认为它们可能与您的解释不符?
  • @Cheersandhth.-Alf 是的,但它不会是 ADL,那么 ;)
猜你喜欢
  • 2017-04-15
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多