The program test tag dispatching pattern, where the function process_data(tag, a, b) can accept 2 tags int_tag, float_tag. There are 3 case:
int_tag and a, b are int -> print a + bfloat_tag and a,b are float -> print a * bunsupported for every other caseThe correct implementation is inside comment block. I'm trying to implement it another way, but current code always output unsupported isntead of calling the correct int/float version of process_data.
Why is the current implementation wrong? Compiled with -std=c++20
#include <iostream>
#include <type_traits>
#include <vector>
#include <string>
// ------------------------------------------------------------
// Tag types: int_tag and float_tag
// ------------------------------------------------------------
struct int_tag {};
struct float_tag {};
// ------------------------------------------------------------
// 2) Primary template: process_data (generic version)
// ------------------------------------------------------------
// template <typename Tag, typename T, typename U>
// void process_data(Tag tag, const T& a, const U& b) {
// std::cout << "<unsupported type>" << "\n";
// }
template <typename Tag, typename A, typename B, typename Enable = void>
void process_data(Tag tag, const A& a, const B& b) {
std::cout << "<unsupported type>" << "\n";
}
// ------------------------------------------------------------
// 3) Tag-dispatched function for int_tag (sum two integral numbers)
// ------------------------------------------------------------
// template <typename T, typename U>
// std::enable_if_t<std::is_integral_v<T> && std::is_integral_v<U>, void>
// process_data(int_tag, const T& a, const U& b) {
// std::cout << "Result: " << (a + b) << "\n"; // Sum for integral types
// }
template <typename A, typename B, std::enable_if_t<std::is_integral_v<A> && std::is_integral_v<B>>>
void process_data(int_tag, const A& a, const B& b) {
std::cout << "Result: " << (a + b) << "\n";
}
// ------------------------------------------------------------
// 4) Tag-dispatched function for float_tag (multiply two floating-point numbers)
// ------------------------------------------------------------
// template <typename T, typename U>
// std::enable_if_t<std::is_floating_point_v<T> && std::is_floating_point_v<U>, void>
// process_data(float_tag, const T& a, const U& b) {
// std::cout << "Result: " << (a * b) << "\n"; // Product for floating-point types
// }
template <typename A, typename B, std::enable_if_t<std::is_floating_point_v<A> && std::is_floating_point_v<B>>>
void process_data(float_tag, const A& a, const B& b) {
std::cout << "Result: " << (a * b) << "\n";
}
// ------------------------------------------------------------
// TESTS (do NOT change)
// ------------------------------------------------------------
int main() {
bool all_ok = true;
// Test: process_data for int_tag
std::cout << "[process_data for int_tag]\n";
std::cout << "int: ";
process_data(int_tag{}, 10, 20); // Should print: Result: 30
std::cout << "float: ";
process_data(float_tag{}, 3.14f, 2.0f); // Should print: Result: 6.28
std::cout << "string: ";
process_data(int_tag{}, "Hello", "World"); // Should print: <unsupported type>
return 0;
}
Issue is with your enable_if_t usage.
With false condition SFINAE rejects it as intended, but with true condition
it becomes template <typename A, typename B, void> which is also rejected by SFINAE.
It should be something like:
template <typename A,
typename B,
std::enable_if_t<std::is_integral_v<A> && std::is_integral_v<B>>* = nullptr>
// ^^^^^^^^^^^
// add valid type and default
// or
// std::enable_if_t<std::is_integral_v<A> && std::is_integral_v<B>, bool> = true>
void process_data(int_tag, const A& a, const B& b) {
std::cout << "Result: " << (a + b) << "\n";
}
Your confusion is from
// 2) Primary template: process_data (generic version)
template <typename Tag, typename A, typename B, typename Enable = void>
void process_data(Tag, const A&, const B&);
There are no partial specializations for functions.
You just add overloads.
The typename Enable = void is so superfluous.
With C++20, you might simplify to:
template <std::integral A, std::integral B>
void process_data(int_tag, const A& a, const B& b) {
std::cout << "Result: " << (a + b) << "\n";
}
template <std::floating_point A, std::floating_point B>
void process_data(float_tag, const A& a, const B& b) {
std::cout << "Result: " << (a * b) << "\n";
}