Home > Software engineering >  Is it possible to explicitly instantiate a templated function that returns an unspecified type?
Is it possible to explicitly instantiate a templated function that returns an unspecified type?

Time:12-16

I'm trying to perform explicit instantiation of templated functions to improve accuracy of my code coverage results. I know how to do this when the return type is known. I can indirectly achieve the desired outcome through a definition of a wrapper function that returns void. Is it possible to achieve the outcome without this additional function definition?

#include <concepts>

template <typename T>
struct Gen {
    T operator()() const { return T{}; }
};
template <std::default_initializable T>
Gen<T> buildKnown() {
    return Gen<T>{};
}
template <std::default_initializable T>
std::invocable auto buildConstrained() {
    return Gen<T>{};
}
template <std::default_initializable T>
std::invocable auto buildInternal() /* -> decltype(??) */ {
    struct Local : Gen<T> {};
    return Local{};
}

// Perform explicit instantiation of each templated function.
template Gen<int> buildKnown<int>();        // works
template Gen<int> buildConstrained<int>();  // somewhat surprised this doesn't work.
template auto buildInternal<int>();         // not surprised this doesn't work

// Indirectly instantiate buildInternal through a wrapper function.
template <typename T> void buildInternalWrapper() { buildInternal<T>(); }
template void buildInternalWrapper<int>();

EDIT: Fixed type constraint (was originally std::invocable<int>) that makes the question more confusing.

CodePudding user response:

Well, first of all, your deduced return type constraints are wrong. Consider for example this function template that you wrote:

template <std::default_initializable T>
std::invocable<T> auto buildConstrained() {
    return Gen<T>{};
}

What will happen if this is instantiated with T = int? It will become

std::invocable<int> auto buildConstrained() {
    return Gen<int>{};
}

The placeholder type specifier std::invocable<int> auto means that the type needs to be deduced, and, if we call this deduced type U, then the constraint std::invocable<U, int> must be satisfied. So this function definition is only well-formed if std::invocable<Gen<int>, int> is satisfied. But it's not; Gen<int> is invocable with 0 arguments, not with 1 int argument.

I'm guessing you meant:

template <std::default_initializable T>
std::invocable auto buildConstrained() {
    return Gen<T>{};
}

Having made such an amendment, we can now declare the explicit instantiation:

template std::invocable auto buildConstrained<int>();

An explicit instantiation of a function template that uses a placeholder type specifier must also use the same placeholder type specifier, not the deduced type.

  • Related