How template specialization works in this case in C++20:
#include <iostream>
template <typename T, typename E> void AFuncToTest()
{
std::cout << "it fails!" << std::endl;
}
template <typename T, std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>> void AFuncToTest()
{
std::cout << "int value!" << std::endl;
}
int main()
{
AFuncToTest<int, int8_t>();
}
OUTPUT: "it fails!"
Why the code is printing it fails! since I defined int8_t e.g.??
The AFuncToTest<int, int8_t>(); doesnt match: template <typename T, std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>> void AFuncToTest()
If I change the way the algorithm is done it also doesnt work like this:
#include <iostream>
template <typename T, typename E> void AFuncToTest()
{
std::cout << "it fails!" << std::endl;
}
template <typename T, int8_t> void AFuncToTest()
{
std::cout << "int value!" << std::endl;
}
int main() {
AFuncToTest<int, int8_t>();
}
OUTPUT: "it fails!"
So I think I'm missing some concept of the templates that is not matching the right result
It looks like you are trying to activate function AFuncToTest
when its template argument T
is either int8_t
or int16_t
. I say this, because the condition you supply tests only parameter T
. It does not test parameter E
.
// from the OP:
std::enable_if_t<std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>>
If that is true, then you do not need template parameter E
. Parameter T
, alone, is sufficient. Parameter E
was apparently intended to capture the result of "enable_if_t", but it is not needed.
The following example uses function overloading, rather than function template specialization. The enable_if_t
parameters it contains allow you to "turn on" or "turn off" the competing overloads.
When the condition in an enable_if_t
parameter evaluates to false
, enable_if_t
becomes malformed, and the overload using it will not compile. Due to SFINAE, however, the failure is ignored (and does not trigger a compilation error), while the overload is discarded. See CppReference.
// main.cpp
#include <cstdint>
#include <iostream>
#include <type_traits>
// This is a "variable template."
template< typename T >
inline constexpr bool is_int8_or_int16
= std::is_same_v<T, std::int8_t> || std::is_same_v<T, std::int16_t>;
// Note the "logical not" operator (i.e., the exclamation point).
template< typename T, std::enable_if_t<!is_int8_or_int16<T>, bool> = true >
void AFuncToTest()
{
std::cout << "it fails!" << std::endl;
}
// There is no "logical not" here.
template< typename T, std::enable_if_t<is_int8_or_int16<T>, bool> = true >
void AFuncToTest()
{
std::cout << "int value!" << std::endl;
}
int main()
{
AFuncToTest<int>(); // outputs "it fails!"
AFuncToTest<std::int8_t>(); // outputs "int value!"
}
// end file: main.cpp
it fails!
int value!
In a comment below, @Jarod42 points out that the OP is using C++20, and provides the following solution that is valid in C++20 (and later). This solution uses a requires clause, to place a constraint on template parameter T
.
// main.cpp
// Solution by @Jarod42
#include <iostream>
#include <type_traits>
template <typename T> void AFuncToTest()
{
std::cout << "it fails!" << std::endl;
}
template <typename T>
requires (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>)
void AFuncToTest()
{
std::cout << "int value!" << std::endl;
}
int main()
{
AFuncToTest<int8_t>();
AFuncToTest<int32_t>();
}
// end file: main.cpp
As above, this works through function overloading.
AFuncToTest
. Since the other criteria for selecting an overload are equal (in the case of function AFuncToTest
), the compiler chooses the overload that is "more" constrained.The notion of "more" constrained has a precise definition which involves the idea of one constraint subsuming another. In most cases, however, all you need is an intuitive understanding to know whether a given overload is "more" constrained than another.