Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Template instantiation with undefined member function return types

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

This compiles fine on Clang and GCC. Wrapper<int> gets instantiated even though the return type of Wrapper<int>::call_f() can not be deduced (there is no int::f()). It fails only when w2.call_f() gets called.

Is this part of the C++ standard, and can it be expected to work on all compilers?

like image 996
tmlen Avatar asked Dec 11 '22 15:12

tmlen


1 Answers

Yes, this is part of the C++ standard.

The rules of template instantiation are long and complex, but the short version is that a member function of a template class is only instantiated when needed. If nothing calls it, or tries to take a pointer to it, or explicitly instantiates it (and probably a few other cases that I'm forgetting), then it won't be instantiated and your code is well-formed.

As @dyp points out, it is only the declaration of the member function which is instantiated when the class definition is instantiated ([temp.inst]/1), but the return type deduction is only carried out when the function definition is instantiated ([dcl.spec.auto]/12).

This is extremely helpful both for minimising the overhead of templates and being permissive about type requirements. It's this feature which lets you do something like this:

struct Foo {
    //no default constructor
    Foo(int);
};

std::vector<Foo> foos;

Some std::vector functions (resize, for example) require T to be default-constructible, but as long as you don't call those functions you can still use other features of std::vector.

like image 108
TartanLlama Avatar answered Dec 13 '22 05:12

TartanLlama