开发者

C++ newbie question: designing a function to return a vector of either string or double--overload or template?

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 q开发者_Go百科uestion, 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.


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


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.


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.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜