C ++新手问题:设计一个函数来返回字符串或双重过载或模板的向量?

时间:2011-02-04 12:34:52

标签: c++ templates overloading

我编写了一个在文本文件中搜索名称的函数。它返回vector,其中向量的每个元素都是不同的名称。

现在,我想在同一个文本文件中搜索数字,并在向量中返回数字。

这可能是一个愚蠢的问题,但我想知道最好的方法是什么。通过编写第二个函数来重载函数,该函数返回向量或通过将类型替换为T来将我已经写入的函数转换为模板,如向量中所示。

我对模板选项感到困惑的原因是我不确定字符串和数字类型(如double和int)是否在模板中兼容。任何提示将不胜感激!感谢。

5 个答案:

答案 0 :(得分:5)

由于函数签名不包含其返回类型,因此不能仅对其返回类型重载函数。

此外,作为这两个函数返回不同类型的数据(一个可以返回的人的名字,对方可能会返回年龄的人),有两个相同的名字似乎是一个语义错误。

但无论如何......

将返回类型部分移回签名

void retrieveData(std::vector<std::string> & data) ;
void retrieveData(std::vector<double>      & data) ;

void foo()
{
   std::vector<std::string> strings ;
   std::vector<double> doubles ;

   // retrieve the strings
   retrieveData(strings) ;
   // retrieve the numbers
   retrieveData(doubles) ;
}

此解决方案是最好的两个,因为超负荷工作“原样”,因为它避免了载体的拷贝(我使用C ++ 03,在这里...在C ++ 0x中,人们会使用移动语义避免潜在的额外副本。)

使返回类型成为签名(非模板版本)的一部分

std::vector<std::string> retrieveData(std::string * dummy) ;
std::vector<double>      retrieveData(double      * dummy) ;

void foo()
{
   // retrieve the strings
   std::vector<std::string> strings = retrieveData((std::string *) NULL) ;
   // retrieve the numbers
   std::vector<double> doubles = retrieveData((double *) NULL) ;
}

这里,虚拟参数的指针值未在函数体中使用。它的用途是使编译器能够找到正确的重载。

这是丑陋的(不提的部分上返回由复制C ++ 03的载体,但是这是出于话题),但它确实工作,是一个可行的回答你的问题。

使返回类型成为签名(模板版本)的一部分

// declared, but not defined
template<typename T>
std::vector<T> retrieveData() ;
// defined
template<>
std::vector<std::string> retrieveData<std::string>() ;
// defined
template<>
std::vector<double>      retrieveData<double>() ;

void foo()
{
   // retrieve the strings
   std::vector<std::string> strings = retrieveData<std::string>() ;
   // retrieve the numbers
   std::vector<double> doubles = retrieveData<double>() ;
}

这里,模板参数由用户给出,从而为编译器提供足够的信息来选择正确的重载

P.S。:这个答案与我在这里给出的答案非常相似:Overload a C++ function according to the return value

答案 1 :(得分:4)

我只使用不同的名称创建两个不同的函数,如findStrings()和findNumbers()。按返回类型重载不起作用,模板在这里没有意义,最重要的是,这些函数只做不同的事情。

但是,如果需要重载,我会这样做:

bool findInFile(const std::string &fileName, std::vector<int> &result);
bool findInFile(const std::string &fileName, std::vector<std::string> &result);

这样重载就可以了,如果你想避免在出现故障时抛出异常,它也有一个很好的返回成功或失败指示的属性。只需将其替换为void。但是,如果您不需要将结果存储在变量中,而是将其传递给某个函数,则此方法的缺点是难以使用。

答案 2 :(得分:3)

由于你只有1次重载,我会使用函数重载而不是模板。我认为在这种情况下,仅仅为了重载函数一次就不能添加一堆代码。

答案 3 :(得分:2)

可能模板函数的案例如下:

template <typename T, typename OutputIterator>
void readTokens<T>(OutputIterator oi) {
    for each token in the file {
        std::stringstream ss(token);
        T t;
        if (ss >> t) {
           *oi++ = t;
        }
    }
}

然后您可以执行readTokens<string>readTokens<int>readTokens<double>或您将来关注的具有流提取运算符的任何其他类型(其字符串表示形式不包含)空格,或者用于分隔文本文件中的项目的其他任何内容。)

这仅适用于将文件拆分为标记/项目的方法相同而不管它们将被读取的类型。我说“也许”的原因是,首先将文件读入非模板函数中的字符串可能会更好,然后有一个单独的模板函数尝试将它们转换为int / double / whatever,过滤掉那些无法转换。你会注意到上面的代码使用T = string并不是非常有效,而且坦率地写了它你不可能用你目前感兴趣的两种类型(int和string)来测试它,所以你可能会为以后存储麻烦。

我对你的函数接口进行了第二次更改,即将结果写入迭代器而不是按值返回向量。这是一个独立的更改,但它是模板函数的常见技巧,因为它意味着调用者可以将结果写入向量或他们喜欢的任何其他容器,或者可能处理它们而不是一次性存储它们(例如,如果全部我们需要的是将它们写入流中)。要获得原始行为,将结果放在向量中,您可以这样称呼它:

std::vector<int> v;
readTokens<int>(std::back_inserter(v));

答案 4 :(得分:1)

模板在这里没有意义,因为函数的代码(可能)取决于它的参数类型。所以你无论如何都必须专门化模板,你也可以只使用重载。