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.