C++ 模板参数困境
我左右为难。假设我有一个模板类:C++ 模板参数困境,c++,templates,C++,Templates,我左右为难。假设我有一个模板类: template <typename ValueT> class Array { public: typedef ValueT ValueType; ValueType& GetValue() { ... } }; 模板 类数组 { 公众: typedef ValueT ValueType; ValueType&GetValue() { ... } }; 现在我想定义一个函数,该函数接收对类
template <typename ValueT>
class Array
{
public:
typedef ValueT ValueType;
ValueType& GetValue()
{
...
}
};
模板
类数组
{
公众:
typedef ValueT ValueType;
ValueType&GetValue()
{
...
}
};
现在我想定义一个函数,该函数接收对类的引用并调用函数GetValue()。我通常考虑以下两种方式:
方法1:
template <typename ValueType>
void DoGetValue(Array<ValueType>& arr)
{
ValueType value = arr.GetValue();
...
}
模板
void DoGetValue(数组和arr)
{
ValueType value=arr.GetValue();
...
}
方法2:
template <typename ArrayType>
void DoGetValue(ArrayType& arr)
{
typename ArrayType::ValueType value = arr.GetValue();
...
}
模板
void DoGetValue(数组类型和arr)
{
typename ArrayType::ValueType value=arr.GetValue();
...
}
这两种方法几乎没有区别。即使调用这两个函数,看起来也完全相同:
int main()
{
Array<int> arr;
DoGetValue(arr);
}
intmain()
{
阵列arr;
DoGetValue(arr);
}
现在,这两个中哪一个是最好的?我能想到一些利弊:
方法1优点:
参数是一个真实的类,而不是模板,因此用户更容易理解接口-参数必须是数组这一点非常明确。在方法2中,您只能从名称猜出它。我们在函数中使用ValueType,因此这种方式比隐藏在数组中时更清晰,并且必须使用scope操作符访问它
此外,typename关键字可能会让许多不懂模板的程序员感到困惑
方法2优点:
此函数更符合其目的。当我认为如果是这样的话,我真的不需要类是数组。我真正需要的是一个类,它有一个方法GetValue和一个类型ValueType。这就是全部。也就是说,这种方法更通用
此方法也较少依赖于数组类中的更改。如果数组的模板参数发生更改怎么办?为什么它会影响DoGetValue?它并不真正关心数组是如何定义的
每次遇到这种情况,我都不知道该选择什么。你的选择是什么?第二个更好。在第一个的“优点”中,您说,“非常明确的是,参数必须是数组”。但是说参数必须是数组是一个不必要的限制。在第二个示例中,任何具有适当GetValue函数的类都可以。因为这是一个不必要的限制,所以最好删除它(第二个限制),而不是将其显式化(第一个限制)。您将编写更灵活的模板,这在将来希望从非数组的内容中获取值时非常有用。我的朋友又提出了两种更极端的方法: 方法3:使您能够使用没有::ValueType的类型
template <typename ArrayType, typename ValueType = ArrayType::ValueType>
void DoGetValue(ArrayType& arr)
{
ValueType value = arr.GetValue();
...
}
模板
void DoGetValue(数组类型和arr)
{
ValueType value=arr.GetValue();
...
}
方法4:强制数组成为具有一个模板参数的类的一种很酷的方法
template <template <typename> class ArrayType, typename ValueType>
void DoGetValue(ArrayType<ValueType>& arr)
{
ValueType value = arr.GetValue();
...
}
模板
void DoGetValue(数组类型和arr)
{
ValueType value=arr.GetValue();
...
}
如果您的函数非常特定于ArrayType
,并且没有其他模板能够满足其接口要求,请使用#1,因为它既短又具体:一般读者会被告知它在ArrayType
上运行
如果其他模板可能与DoGetValue
兼容,请使用#2,因为它更通用
但是没有必要痴迷,因为在它们之间转换很容易。一般来说,我同意,我自己倾向于第二种选择,但在一个封闭的生态系统中,我实际上想强制使用数组,这种限制可能会成为一种优势。是的,可能我应该添加一些含糊不清的说法——当我说“更好”时,我的意思是,这是第一选择,除非有一些特定的因素,而不是在问题中陈述的,在特定情况下,这会使其他事情变得更好。例如,如果您使用15种不同的Array函数,并且在开发过程中执行Array的接口进行了大量更改,并且为了使DoGetValue始终更改为匹配,那么让人们在另一个类中使用DoGetValue意味着他们也必须不断更改另一个类,这可能比让他们使用数组更糟糕。@FireAphis,如果你想限制使用数组(我可以想象,当你重载的是运算符时,减少歧义的机会是一个很好的理由),那么为什么不使用两种世界中最好的一种:接受数组,但是仍然使用::ValueTypeMethod 3需要
typename数组类型::ValueType
为什么要限制参数的数量?这看起来像是在射自己的脚。@Potatostater,你当然是对的。这些解决方案更多的是出于学术兴趣:为了泛型,我也会考虑“改变”你自己类的接口,并试图模仿STL容器,这样你就可以在STL容器上使用你自己的函数模板作为奖励> <代码> ValueYyType < /代码>,而不是<代码> ValueType < /C>front
或back
而不是GetValue
(或者可能是at
,如果它需要一个位置?)等等。@Matthieu,说得对!这是我应该考虑的问题。我从来没有想过STL模仿的可能性。它确实很有用,甚至可以在代码中提供视觉线索。谢谢。我喜欢“没用的困扰”部分:)