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

发布于 2024-10-16 06:25:06 字数 249 浏览 1 评论 0原文

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

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

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

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

I have written a function that searches a text file for names. It returns vector, where each element of the vector is a different name.

Now I would like to search the same text file for numbers and return the numbers in a vector.

This is probably a dumb question, but I'm wondering what the best approach would be. Overload the function by writing a second function that returns a vector or turning the function I have already written into a template by replacing the type with T, as in vector.

The reason I'm confused about the template option is that I'm not sure if strings and numerical types like double and int are compatible in a template. Any tips would be appreciated! Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

时光病人 2024-10-23 06:25:06

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

此外,由于这两个函数返回不同类型的数据(一个可以返回人名,另一个可以返回人的年龄),因此两个函数具有相同的名称似乎是语义错误。

但无论如何......

将返回类型部分移回到签名中

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.:这个答案与我在这里给出的答案非常相似:根据返回值重载C++函数

As a function signature does not include its return type, you can't overload a function only on its return type.

Also, as the two functions returns different kind of data (one could return person names, the other could return the persons ages), having the same name for both seems a semantic error.

But anyway...

Move the return type part back into the signature

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) ;
}

This solution is best both because the overload work "as is", and because it avoids a copy of the vector (I am using C++03, here... In C++0x, one would use move semantics to avoid the potential extra copy).

Make the return type part of the signature (non-template version)

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) ;
}

Here, the dummy parameter's pointer value is not used in the function body. It's use is to enable the compiler to find the right overload.

This is ugly (not mentioning the part on returning by copy a vector in C++03 but this is out of topic), but it does the work and is a viable answer to your question.

Make the return type part of the signature (template version)

// 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>() ;
}

Here, the template parameter is given by the user, thus giving the compiler enough information to choose the right overload

P.S.: This answer is quite similar to the one I gave here: Overload a C++ function according to the return value

我不在是我 2024-10-23 06:25:06

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

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

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

这样重载就可以工作,并且如果您想避免在失败时抛出异常,它还有一个很好的属性,即返回成功或失败指示符。否则只需将其替换为 void 即可。但这种方法的缺点是,如果您不需要将结果存储在变量中,而是将其传递给某个函数,则使用起来很尴尬。

I'd just make two different functions with different names like findStrings() and findNumbers(). Overloading by return type doesn't work, templates make no sense here, and, most importantly, these functions just do different things.

However, if overloading is desired, I would do it like this:

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

This way overloading will work, and it also has a nice property of returning success or failure indicator if you want to avoid throwing exceptions in case of failure. Just replace it with void otherwise. But this approach has the disadvantage of being awkward to use if you need not to store the result in a variable, but pass it to some function for example.

转身以后 2024-10-23 06:25:06

由于您只需要做 1 个重载,因此我会使用函数重载而不是模板。我认为在这种情况下,仅仅为了重载函数一次而添加一堆代码是不合理的。

Since you have only 1 overload to do, I would go with function overload instead of templates. I think that in this case it doesn't justify adding a bunch of code just to overload a function once.

风情万种。 2024-10-23 06:25:06

也许模板函数的情况如下:

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, readTokens, readTokens,或者您将来想要发明的具有流提取运算符的任何其他类型(并且其字符串表示形式不包含空格,或者您用来分隔文本文件中的项目的任何其他类型) )。

仅当将文件分割为标记/项目的方法相同时才适用,无论它们将被读取为何种类型。我说“也许”的原因是,最好首先将文件读入非模板函数中的字符串,然后使用一个单独的模板函数尝试将它们转换为 int/double/无论什么,过滤掉那些转换失败。您会注意到,上面的代码对于 T = string 来说并不是非常有效,并且坦率地说,在编写它之后,您不太可能使用除您当前感兴趣的两种类型(int 和 string)之外的任何类型来测试它,因此你可能会为以后埋下麻烦。

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

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

There is maybe a case for a template function as follows:

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;
        }
    }
}

Then you can do readTokens<string>, readTokens<int>, readTokens<double>, or any other type you care to invent in future that has a stream extraction operator (and whose string representation doesn't contain spaces, or whatever else it is you use to delimit items in your text file).

This only applies if the means of splitting up the file into tokens/items is the same regardless of the type that they're going to be read as. The reason I say "maybe" is that it might be better to first read the file into strings in a non-template function, then have a separate template function that tries to convert them to int/double/whatever, filtering out the ones that fail to convert. You'll note that the above code isn't terribly efficient with T = string, and frankly having written it you're unlikely to test it with any types other than the two you're currently interested in (int and string), so you could be storing up trouble for later.

I've made a second change to your function interface, which is to write the results to an iterator rather than returning a vector by value. This is an independent change, but it's a common trick with template functions because it means the caller can have the results written to a vector, or any other container they prefer, or perhaps process them without storing them all at once (for example if all that's needed is to write them to a stream). To get the original behavior, with the results in a vector, you'd call it like this:

std::vector<int> v;
readTokens<int>(std::back_inserter(v));
橘亓 2024-10-23 06:25:06

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

Templates would make no sense here, because the code for the function (presumably) depends on its argument type. So you would have to specialise the template anyway, and you might as well just use overloading for this.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文