c++ - C++ 模板的实例化惰性规则

标签 c++ templates

<分区>

据我所知,模板实例化只有两个“阶段”:

  • 默认(立即),
  • 延迟(懒惰)。

默认用于以下成员:

  • typedef
  • 成员字段。

延迟:

  • 函数(静态或非静态)
  • 嵌套类型。

这是正确的,还是存在一些关于编译器急于实例化模板的陷阱/规则?

最佳答案

完全不正确。模板只有一个阶段 实例化。何时(何地,这是一个重点) 取决于它是如何使用的。我认为你混淆了名字 查找与实例化。

名称查找分两个阶段完成。 (至少如果编译器 符合——VC++ 仍然会出错。)第一个出现 当编译器解析模板定义时;第二 当模板被实例化时。是否看符号 在第一阶段或第二阶段取决于它是否 依赖与否。确定这一点的规则是公平的 复杂,但总的来说:

  • 如果它是一个函数名,它的一个或多个参数 依赖于模板参数,函数名是依赖的, 它将被查找(和函数重载解析) 在实例化时发生,而不是之前。

  • 如果名称由模板参数限定(例如 T::something,其中 T 是模板参数),它是 依赖。

  • 在类模板的成员中,如果有依赖基 类(在某种程度上依赖于模板的基类 参数),this-> 右边的任何内容都是从属的。

(实际规则要复杂得多,但 以上是一个粗略的近似值,可能对大多数人来说已经足够了 用途。)

编辑:

只是一些区别的例子:

class MyType {};

void func0( double d )
{
    std::cout << "called func0( double )" << std::endl;
}

void func1( double, MyType const& )
{
    std::cout << "called func1( double )" << std::endl;
}

template <typename T>
int funcT( T const& param )
{
    func0( 42 );            //  non-dependent
    func1( 42, param );     //  dependent
}

void func0( int d )
{
    std::cout << "called func0( int )" << std::endl;
}

void func1( int, MyType const& )
{
    std::cout << "called func1( int )" << std::endl;
}

int
main()
{
    funcT( MyType() );
    return 0;
}

输出应该是

called func0( double )
called func1( int )

funcT中对func0的调用是非依赖的,所以名字查找 只发生在模板被定义的地方,并且 稍后,当它被实例化时。那时,有 只有一个 func0,所以它被调用。

funcT 中对 func1 的调用是依赖的(因为它的第二个 参数取决于模板参数),所以会有一个 在实例化时进行额外的名称查找 (在本例中,紧跟在 main 之后)。这个额外的 查找仅使用 ADN,但由于其中一个参数已定义 在全局命名空间中,ADN 将在全局命名空间中查找,并找到 func1 的第二个声明(然后将是 由重载决议选择,因为它是更好的匹配)。

请注意,我无法验证这一点,因为目前,我只 可以访问已损坏的 VC++11。而且它很微妙,所以 我很可能错过了什么。一般规则是 以避免此类歧义。

注意如果func0没有在模板之前声明 定义,代码不应编译。根据经验,当 从准标准编译器(或 VC++)转移,这是最 错误的常见原因。

另一种常见情况有时会让人感到意外:

template <typename Base>
class Derived : public Base
{
public:
    Derived()
    {
        init();         //  Non-dependent lookup, will NOT find any
                        //  function init() in Base!!!
        this->init()    //  Dependent lookup, WILL find
                        //  Base::init, if it exists.
        Base::init()    //  Also dependent.
    }
};

通常,如果您正在使用此模式,并在 像 VC++ 这样的准标准编译器,你会得到很多 迁移到更现代的编译器时出现编译器错误。 通过添加 this-> 可以很容易地修复它们。然而,如果 还有一个全局函数 init 在模板时可见 已定义,您将调用此函数,不是 基类。如果您为函数选择有意义的名称, 并将全局函数放在命名空间中,你可能不会得到 语义的这种无声变化经常受到打击,但要注意。

关于c++ - C++ 模板的实例化惰性规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15632546/

相关文章:

C++ 模板或运算符?

c++ - ModelResetter RAII 对象

C++ 动态/静态数组作为函数的参数

c++ - 有没有更短的方法来初始化 QByteArray?

c++ - ODBC Execute/Fetch of SQL 2005 存储过程结果集不能使用表@variable

c++ - 在带有可变参数模板的基于模板的类中进行完善转发?

c++ - 为什么带有指向模板仿函数指针的 Map 作为 value_type 不起作用?

c++ - 将 stringstream 的内容传递给以 char* 为参数的函数

c++ - USSD 响应的最大长度是多少(不是请求长度)?

C++:未使用嵌入式模板调用析构函数