具有未定义成员函数返回类型的模板实例化

Template instantiation with undefined member function return types

本文关键字:实例化 返回类型 函数 未定义 成员      更新时间:2023-10-16
struct Value {
    using a_type = int;
    a_type f() { return 1; }
};
template<typename T>
struct Wrapper {
    T t;
    auto call_f() { return t.f(); }
};
int main() {
    Wrapper<Value> w;
    Wrapper<int> w2;
    w.call_f();
}

在Clang和GCC上可以很好地编译。即使无法推断出Wrapper<int>::call_f()的返回类型(不存在int::f()), Wrapper<int>也会被实例化。只有当w2.call_f()被调用时才会失败。

这是c++标准的一部分吗?它能在所有的编译器上工作吗?

是的,这是c++标准的一部分。

模板实例化的规则又长又复杂,简单来说就是模板类的成员函数只在需要的时候实例化。如果没有调用它,或者尝试获取指向它的指针,或者显式实例化它(可能还有一些我忘记的其他情况),那么它将不会被实例化,并且您的代码是格式良好的。

正如@dyp所指出的,当类定义被实例化([temp.inst]/1)时,只有成员函数的声明才被实例化,而返回类型的推导只有在函数定义被实例化([dcl.spec.auto]/12)时才进行。

这对于最小化模板开销和允许类型需求都非常有帮助。这个特性可以让你做这样的事情:

struct Foo {
    //no default constructor
    Foo(int);
};
std::vector<Foo> foos;

一些std::vector函数(例如resize)要求T是默认可构造的,但只要你不调用这些函数,你仍然可以使用std::vector的其他功能。

是的,这是由标准保证的。Wrapper<T>::call_f()只有在被调用时才会被隐式实例化。

$14.7.1/2隐式实例化[temp.inst]:

除非类模板或成员模板的成员已显式实例化或显式特化,否则当在要求成员定义存在的上下文中引用该特化时,该成员的特化将隐式实例化;

$14.7.1/8隐式实例化[temp.inst]:

实现不应隐式实例化函数模板、变量模板、成员模板、非虚成员函数、成员类、类模板的静态数据成员或constexpr if语句([stmt.if])的子语句,除非需要这样的实例化。