I have two related types (duck-typing), and another one that provides similar functionality with a different interface:
namespace a
{
template<typename T>
struct A final {};
using int_t = A<int>;
using float_t = A<float>;
}
namespace b
{
template<typename T>
struct B final {};
using int_t = B<int>;
using float_t = B<float>;
}
namespace foo
{
template<typename T>
struct Foo final {};
using int_t = Foo<int>;
using float_t = Foo<float>;
std::string f(const int_t&)
{
return "f(Foo<int>)\n";
}
std::string g(const int_t&)
{
return "g(Foo<int>)\n";
}
std::string h(const float_t&)
{
return "h(Foo<float>)\n";
}
}
a and b are "related", foo is "similar." Writing a generic f() works, but is too "greedy":
template<typename T>
std::string f(const T&)
{
return "f(T)\n";
}
// ...
int main()
{
a::int_t a_int;
b::int_t b_int;
foo::int_t foo_int;
std::cout << f(a_int); // "f(T)"
std::cout << f(b_int); // "f(T)"
std::cout << f(foo_int); // "f(Foo<int>)"
std::cout << f(314); // NO! ... "f(T)"
std::cout << "\n";
}
To "tighten" that up a bit, I did the following:
template<typename Tab> struct AB final
{
AB() = delete;
};
template<typename T> struct AB<a::A<T>> final
{
AB() = delete;
using type = a::A<T>;
};
template<typename T> struct AB<b::B<T>> final
{
AB() = delete;
using type = b::B<T>;
};
Now I can write g() that only works on a::A<T> and b:B<T>:
template<typename Tab, typename type_ = typename AB<Tab>::type>
std::string g(const Tab&)
{
return "g(Tab)\n";
}
This works as expected:
std::cout << g(a_int); // "g(Tab)"
std::cout << g(b_int); // "g(Tab)"
std::cout << g(foo_int); // "g(Foo<int>)"
//std::cout << g(314); // YES! doesn't compile
std::cout << "\n";
However, I can't figure out how to set things up to write an h() that will only work for either a::A<float> or b::B<float>, but not any other specialization.
template<typename Tab, typename type_ = typename AB<Tab>::type>
std::string h(const Tab&)
{
return "h(Tab<float>)\n";
}
// ...
a::float_t a_float;
b::float_t b_float;
foo::float_t foo_float;
std::cout << h(a_float); // "h(Tab<float>)";
std::cout << h(b_float); // "h(Tab<float>)";
std::cout << h(foo_float); // "h(Foo<float>)"
std::cout << h(a_int); // NO! ... "h(Tab<float>)";
std::cout << h(b_int); // NO! ... "h(Tab<float>)";
//std::cout << h(foo_int); // YES! doesn't compile
//std::cout << h(314); // YES! doesn't compile
I've tried a passing a template as a template, but I can't quite figure out the right syntax.
I'd like something that works in C++14.
You could add a constraint.
C++20:
C++14:
In both versions, you make the function take a template-template parameter (
Tab) and then check ifTab<T>is eithera::float_torb::float_tSFINAE style to allow for otherhoverloads.If you don't need SFINAE to create other
hoverloads, astatic_assertmay be preferable: