Cannot access struct value in std::map [duplicate]

2019-07-16 14:23发布

问题:

This question already has an answer here:

  • c++ “no matching function for call to” error with structure 2 answers

DISCLAIMER: I crafted the MCVE to demonstrate an issue I had with a much larger project that has nothing to do with the weather. I chose the MCVE's names and program goal only to demonstrate the problem in an easy-to-understand way. I know this design would suck for a real-life forecast program.

I am using a std::map to store structs, using std::string as a key.

struct Temps
{
    //Temps(int l = 0, int h = 0)
    Temps(int l, int h)
        :low(l), high(h)
        {}
    int low;
    int high;
};

int main()
{
    std::map<std::string, Temps> forecast;
    forecast.emplace("Monday", Temps(49, 40));
    forecast.emplace("Tuesday", Temps(66, 35));
    forecast.emplace("Wednesday", Temps(78, 40));

    return 0;
}

However, I'm having a problem. I can access the contents of those structs within the map using iterators, as expected...

std::map<std::string, Temps>::iterator it;

it = forecast.find("Monday");
if(it != forecast.end())
{
    std::cout << "Monday's high is " << it->second.high << "." << std::endl;
}

But when I try to access using the [] operator on the map, I run into an error...

if(forecast.find("Tuesday") != forecast.end())
{
    std::cout << "Tuesday's high is " << forecast["Tuesday"].high << std::endl;
}

I get a massive, hideous error, which summarizes down to...

/usr/include/c++/5/tuple|1172|error: no matching function for call to ‘Temps::Temps()’|

I know the problem is neither with the struct or the data. What's going on?

回答1:

This technically could be considered a case of Read the Documentation, which I did in order to solve this for myself. However, the solution might not be entirely obvious right off the bat.

The struct Forecast has a constructor, but it is a constructor that requires two arguments. This causes a conflict with one major behavior of the map's [] operator, which the iterator access does not have:

If k does not match the key of any element in the container, the function inserts a new element with that key and returns a reference to its mapped value. Notice that this always increases the container size by one, even if no mapped value is assigned to the element (the element is constructed using its default constructor).

Because Forecast has no such default constructor, map is attempting to call a function which does not exist.

Had I omitted the constructor on Forecast, the compiler would have given the struct a default constructor, and all would be well. However, since I defined a constructor, the compiler won't do that.

Thankfully, the solution is quite simple: either remove the constructor or add a default constructor. In my case, I can just set default arguments.

struct Temps
{
    Temps(int l = 0, int h = 0)
        :low(l), high(h)
        {}
    int low;
    int high;
};

NOTE: Some may argue that the above example is entirely silly - why not just use the iterator? In this case, that would be a very good idea. Using the [] is probably just wasting time, since I'm searching for one anyway to prevent errors. However, there are cases where [] is the best operator for the job, even if this isn't one.