dimanche 3 novembre 2019

Why can't function templates access forward declared types?

Problem

Consider this code (live version), where I have forward-declared a struct S before a function template Use which ODR-uses it. S is defined immediately after the function, but before any instantiation of Use.

struct S;

template<typename T> void Use(S& s, const T& t) { // ODR-use S
    s.Use(t); // error: member access into incomplete type 'S'
}

struct S {
    template<typename T> void Use(const T&) { }
};

int main() {
    auto s = S{}; Use(s, 2); // Instantiation of Use<...>
}

This does not compile, because apparently, the definition of S is somehow invisible to the Use function.

Rationale

Based on my understanding of function templates after reading §13.9.1 of the latest working draft this shouldn't be a problem, as all types are fully defined when the function template is instantiated.

Although, to me it seems like all dependent types will have access to the definitions available at instantiation, while non-dependent types will use the version available at the function definition. Unfortunately I wasn't able to find the corresponding paragraph in the standard which defines this behavior.

Which part of the standard have I missed to explain this behavior?

Work Around

To work around the issue, I can "refresh" the current definition by using IIFE:

// This is OK:
template<typename T> void Use(S& s, const T& t) {
    [&](auto& s_) { s_.Use(t); }(s);
}

Or alternatively by making a dependent type alias for S:

// This is also OK:
template<typename T, typename SS = S> void Use(SS& s, const T& t) {
    s.Use(t);
}

So now I'm wondering, why doesn't an instantiated template always have access to the latest definition of any type, instead of only the dependent types? As I've shown above, you can force the compiler to update any type by using an immediately evaluated lambda - so why isn't this done automatically?

Aucun commentaire:

Enregistrer un commentaire