【发布时间】:2019-01-13 20:44:56
【问题描述】:
考虑以下几点:
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>;
It turns out 推导指南根本不适用于模板别名,即这会产生编译错误:
float x, y;
my_array a { x, y }; // works
special b { x, y }; // doesn't
我们仍然可以说special<float> b 并感到高兴。但是,假设 T 是一些冗长乏味的类型名称,例如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 };
}
这里没有类型重复,但现在我不能声明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>。它可以通过提供从my_array<2> 到special 的转换运算符来解决,但这意味着我们在它们之间进行循环转换,这(以我的经验)是一场噩梦。同样special在使用列表初始化时需要extra curly braces。
还有其他方法可以模拟类似的东西吗?
【问题讨论】:
-
有特殊继承自数组吗?
-
@NathanOliver 我没有考虑继承,因为如果可以的话,我通常不喜欢创建类层次结构。现在我正在考虑它,这实际上可能是最好的选择。谢谢你。为什么不让它成为一个答案?
-
@NathanOliver 我添加了一些关于使用继承的想法。
-
我越来越相信 C++ 需要区分强类型别名和弱类型别名。
-
请参阅 Class template argument deduction not working with alias template,其中有来自编辑器的答案,其中引用了通过别名模板使 CTAD 在 C++20 中可行的更改。
标签: c++ templates c++17 template-argument-deduction template-aliases