Reputation: 60430
I would like to check whether a function template can be instantiated for a given type. As an example, for the template:
template<typename T> void f() { T{}; }
I would like to assert that f<int>
is instantiatable, and f<S>
is not, where S
is some type that would cause the definition of f
to fail compilation if instantiated, say:
struct S { ~S() = delete; };
Clearly, if I know what the function body contains, I could write separate checks for each of the statements. However, this approach will not work if the body itself is unknown.
It appears that I can't use the usual sfinae approach to do this, as the function body isn't checked for deduction failures. Here's the attempt to play with.
Indeed, temp.deduct.8 seems to explicitly disallow this:
Only invalid types and expressions in the immediate context of the function type, its template parameter types, and its explicit-specifier can result in a deduction failure.
and the reason for the "immediate context" constraint seems to be in the very next bullet point:
Note: The intent is to avoid requiring implementations to deal with substitution failure involving arbitrary statements.
If I'm understanding this correctly, and doing this is not possible, can I get an explanation for why this constraint exists? I think arbitrary expressions can be checked for substitution failure by users, so why would this be too much for an implementation?
If I'm misunderstanding this, and it is possible, could I have a solution to this problem?
Upvotes: 0
Views: 469
Reputation: 7369
As said in your question,SFINAE only occurs in "immediate context" and what situation subsumed by "deduction fails" are listed in [temp.deduct]. The error happened in you code will be ill-formed,which is mentioned in the standard:
The substitution into types and expressions can result in effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such effects are not in the “immediate context” and can result in the program being ill-formed.
So,your program would be ill-formed when require instantiation of function template f for type S
.
#include <iostream>
#include <type_traits>
template<typename T> void f() { T a{}; }
struct S { ~S() = delete; };
struct Test{};
template<typename T,typename U = void>
struct is_sutable_type_for_function:std::false_type{};
template<typename T>
struct is_sutable_type_for_function<T,std::void_t<decltype(T{}.~T())>>:std::true_type{};
int main()
{
std::cout<< is_sutable_type_for_function<S>::value<<"\n";
std::cout<< is_sutable_type_for_function<Test>::value<<"\n";
}
Now,the code will work for whether the type T could be used for function template f
Upvotes: 1
Reputation: 41220
In C++ you must put your constraints on the function itself for detection to work:
#include <type_traits>
template<typename T> void f() requires std::is_constructible_v<T> { T{}; }
template<class T, class = void>
struct can_call_f : std::false_type{};
template<class T>
struct can_call_f<T, std::void_t<decltype(f<T>())>>: std::true_type{};
template<class T>
constexpr bool can_call_f_v = can_call_f<T>::value;
struct S { ~S() = delete; };
int main()
{
static_assert(can_call_f_v<int>);
static_assert(!can_call_f_v<S>);
}
Recall that C++ works on types and the body of a function is not considered part of the function's type.
Upvotes: 0