我有两种相关的类型(鸭子类型),另一种提供类似的功能但具有不同的界面:
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
和 b
是“相关”,foo
是“相似”。写一个通用的 f()
可以,但是太“贪婪”了:
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";
}
为了“收紧”一点,我做了以下操作:
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>;
};
现在我可以编写仅适用于
g()
和 a::A<T>
的 b:B<T>
:
template<typename Tab, typename type_ = typename AB<Tab>::type>
std::string g(const Tab&)
{
return "g(Tab)\n";
}
这按预期工作:
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";
但是,我不知道如何设置来编写仅适用于
h()
或 a::A<float>
的 b::B<float>
,但不适用于任何其他专业化。
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
我尝试过将模板作为
template
传递,但我不太清楚正确的语法。
我想要一些可以在 C++14 中运行的东西。
我不知道如何设置来编写一个仅适用于
或a::A<float>
的 h(),但不适用于任何其他专业化。b::B<float>
您可以添加约束。
C++20:
template <template <class> class Tab, class T>
requires(std::same_as<a::float_t, Tab<T>> ||
std::same_as<b::float_t, Tab<T>>)
std::string h(const Tab<T>&) {
return "h(Tab<float>)\n";
}
C++14:
template <template <class> class Tab, class T>
std::enable_if_t<std::is_same<a::float_t, Tab<T>>::value ||
std::is_same<b::float_t, Tab<T>>::value,
std::string>
h(const Tab<T>&) {
return "h(Tab<float>)\n";
}
在这两个版本中,您都使函数采用模板-模板参数 (
Tab
),然后检查 Tab<T>
是否为 a::float_t
或 b::float_t
SFINAE 样式以允许其他 h
重载。
如果您不需要 SFINAE 创建其他
h
重载,则 static_assert
可能更可取:
template <template <class> class Tab, class T>
std::string h(const Tab<T>&) {
static_assert(std::is_same<a::float_t, Tab<T>>::value ||
std::is_same<b::float_t, Tab<T>>::value,
"must be a::float_t or b::float_t");
return "h(Tab<float>)\n";
}
使实施与您已有的保持一致,您可以这样做:
template<template<typename> typename Tab> struct AB final
{
AB() = delete;
};
template<> struct AB<a::A> final
{
AB() = delete;
using type = a::A<float>;
};
template<> struct AB<b::B> final
{
AB() = delete;
using type = b::B<float>;
};
template<template <typename> typename Tab, typename = typename AB<Tab>::type>
std::string h(const Tab<float>&)
{
return "h(Tab<float>)\n";
}
那么不应该编译的东西就不会编译。
注意,里面的
using type = a::A<float>;
仅用于识别。还可以写
template<template <typename> typename Tab, typename = typename AB<Tab>::type>
std::string h(const Tab<int>&)
{
return "h(Tab<int>)\n";
}
不改变
AB
。
我个人会在这个设计上使用
static_assert
或enable_if
。尽管如此,这是一个值得拥有的选择。