C ++:编译器仅在需要时实例化模板函数?

时间:2014-09-17 12:01:20

标签: c++ templates

考虑以下简单模板功能:

template <typename T>
int compare(const T& lhs, const T& rhs) {
    if (lhs<rhs) {
        return -1;
    }
    else if (rhs<lhs) {
        return 1;
    }
    else {
        return 0;
    }
}

我的课程讲师解释说,当我们使用模板函数时,我们应该隐式或显式地指定绑定到模板参数的模板参数:

#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;

int main() {
    // implicitly specifying that T is int
    cout<<compare(2,3)<<endl;

    // explicitly specifying that T is string
    cout<<compare<string>(string("something"),string("another"))<<endl;
}

推导出正确模板的过程称为“实例化”;实际模板参数用于生成稍后在运行时运行的相应版本的特定实例(在本例中为函数)。
他还提到实例化是“按需”发生的;例如,最后一个代码段将生成两个比较函数实例,一个用于int,另一个用于string
这让我很奇怪,为什么编译器会抱怨这样的事情:

#include<iostream>
#include<string>
using std::cout;
using std::endl;
using std::string;


template <typename T>
int compare(const T& lhs, const T& rhs) {
    if (lhs<rhs) {
        return x; // deliberate compile-time error; x cannot be resolved
    }
    else if (rhs<lhs) {
        return 1;
    }
    else {
        return 0;
    }
}

int main() {
    // no calls for compare are made here
}

你认为编译器不应该对上面的编译时错误有任何问题,因为compare永远不会被实例化。但是,这不会编译...
由于没有提供模板参数,因此该模板函数对编译器没有任何意义(这是一个简单的比较函数,但考虑一个模板函数,它主要依赖于模板参数的类型......)
那么究竟什么是“实例化”呢?

2 个答案:

答案 0 :(得分:5)

当编译器看到你的模板时,它已经在某种程度上解析并分析 。确切地说,已经完成的工作非常复杂,但除此之外,(模板参数无关)名称查找发生。这意味着编译器已经尝试找出x是什么,并且他失败了。

编译器试图解决的一个例子是T::x究竟是什么,因为依赖 T,你的模板参数。表达式x 不依赖于任何模板参数,这基本上是x无法编译和T::x will succeed的关键区别:

template <typename T>
int compare(const T& lhs, const T& rhs) {
    if (lhs<rhs) {
        return T::x;
    }
    else if (rhs<lhs) {
        return 1;
    }
    else {
        return 0;
    }
}

当然,对于大多数类型T,这将无法实例化,但这不是关键所在。你甚至可以为所有T编写一个实例化失败的模板,但是这个问题的答案超出了范围。

答案 1 :(得分:1)

§14.6[temp.res] / p10,重点补充:

  

如果名称不依赖于模板参数(如   14.6.2),该名称的声明(或声明集)应在名称出现在模板中的范围内   定义;名称与声明(或声明)绑定   在那一点上找到并且此绑定不受声明的影响   在实例化时可见。

如果模板格式不正确,如果不能为其生成有效的专业化,则违反此规则需要诊断(因为它没有“无需诊断”)。

在您的代码中,x是一个非依赖名称,因此compare模板违反了此规则,并且需要符合要求的实现来生成诊断。