考虑以下:
template <typename T, std::size_t N>
struct my_array
{
T values[N];
};
我们可以提供扣除指南my_array
,例如
template <typename ... Ts>
my_array (Ts ...) -> my_array<std::common_type_t<Ts...>, sizeof...(Ts)>;
现在,假设它my_array<T, 2>
有一些非常特殊的含义(但只是含义,接口和实现保持不变),所以我们想给它一个更合适的名字:
template <typename T>
using special = my_array<T, 2>;
事实证明,推导指南根本不适用于模板别名,即这会产生编译错误:
float x, y;
my_array a { x, y }; // works
special b { x, y }; // doesn't
我们仍然可以说special<float> b
并感到高兴。但是,假设这T
是一些冗长乏味的类型名称,例如 eg std::vector<std::pair<int, std::string>>::const_iterator
。在这种情况下,在这里进行模板参数推导将非常方便。所以,我的问题是:如果我们真的想special
成为(在某种意义上)my_array<T, 2>
与
我提前为一个有点含糊的问题道歉。
我想出了几个解决方案,但都有严重的缺点。
1)special
用相同的接口制作一个单独的不相关的类,即
template <typename T>
struct special
{
T values[2];
};
template <typename T>
special (T, T) -> special<T>;
这种重复看起来很尴尬。此外,而不是编写函数,如
void foo (my_array<T, N>);
我被迫要么复制它们
void foo (my_array<T, N>);
void foo (special<T>);
或做
template <typename Array>
void foo (Array);
并且依赖于这些类的接口是相同的。我一般不喜欢这种代码(接受任何东西并完全依赖鸭子打字)。它可以通过一些 SFINAE/概念来改进,但这仍然感觉很尴尬。
2)做special
一个功能,即
template <typename T>
auto special (T x, T y)
{
return my_array { x, y };
}
这里没有类型重复,但现在我不能声明 type 的变量special
,因为它是一个函数,而不是一个类型。
3) 保留special
模板别名,但提供类似 C++17 之前的make_special
函数:
template <typename T>
auto make_special (T x, T y)
{
return my_array { x, y };
// or return special<T> { x, y };
}
它在某种程度上有效。尽管如此,这不是一个演绎指南,并且将使用演绎指南的类与此make_XXX
功能混合听起来令人困惑。
4) 正如@NathanOliver 所建议的,使special
继承自my_array
:
template <typename T>
struct special : my_array<T, 2>
{};
这使我们能够为 提供单独的推导指南special
,并且不涉及代码重复。但是,编写如下函数可能是合理的
void foo (special<int>);
不幸的是,这将无法接受my_array<2>
。它可以通过提供转换运算符 from my_array<2>
to 来解决special
,但这意味着我们在这些之间进行循环转换,这(以我的经验)是一场噩梦。使用列表初始化时还需要special
额外的花括号。
还有其他方法可以模仿类似的东西吗?