ZoOl007 ZoOl007 - 2 months ago 18
C++ Question

C++14 using auto keyword in a method's definition

I have several

std::unordered_maps
. They all have an
std::string
as their key and their data differs. I want to make a csv string from a given map's keys because that data needs to be sent over the wire to a connected client. At the moment I have a method for each individual map. I wanted to make this generic and I came up with the following :

std::string myClass::getCollection(auto& myMap) {
std::vector <std::string> tmpVec;
for ( auto& elem : myMap) {
tmpVec.push_back(elem.first);
}
std::stringstream ss;
for ( auto& elem : tmpVec ) {
ss << elem <<',';
}
std::string result=ss.str();
result.pop_back(); //remove the last ','
return result;
}


I compile with gcc 6.1.0 and -std=c++14 using eclipse and it compiles but it doesn't link.
The linker complains about undefined reference to
std::__cxx11::getCollection(someMap);


Regardless of the map data and the way I call it, it always tells me :


Invalid arguments ' Candidates are: std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>> getCollection() '



How do I solve this?

Answer

As in C++14 auto parameters are only allowed in lambdas (as per @ildjarn's comment), you can just develop a function template, templateized on the map type, e.g.:

#include <sstream>
#include <string>
#include <vector>

class myClass {
...

template <typename MapType>
std::string getCollection(const MapType& myMap) {
    std::vector <std::string> tmpVec;
    for ( const auto& elem : myMap) {
        tmpVec.push_back(elem.first);
    }
    std::stringstream ss;
    for ( const auto& elem : tmpVec ) {
        ss << elem <<',';
    }
    std::string result=ss.str();
    result.pop_back(); //remove the last ','
    return result;
}

Note also the addition of const for some const-correctness.

Moreover, why not just building the output string directly using the string stream object, without populating an intermediate vector<string> (which is more code, more potential for bugs, more overhead, less efficiency)?

And, since you are just interested in using the string stream as an output stream, using ostringstream instead of stringstream is better as it's more efficient and communicates your intent better.

#include <sstream>  // for std::ostringstream
#include <string>   // for std::string
...

template <typename MapType>
std::string getCollection(const MapType& myMap) {
    std::ostringstream ss;
    for (const auto& elem : myMap) {
        ss << elem.first << ',';
    }
    std::string result = ss.str();
    result.pop_back(); // remove the last ','
    return result;
}