Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 模板参数困境_C++_Templates - Fatal编程技术网

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模仿的可能性。它确实很有用,甚至可以在代码中提供视觉线索。谢谢。我喜欢“没用的困扰”部分:)