Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::map with predicate vs. initialization list

Tags:

c++

stl

I have an std::map<string, string> with a custom predicate:

struct PredIgnoreCase {
  bool operator()(const std::string& str1, const std::string& str2) const {
    std::string str1NoCase(str1), str2NoCase(str2);
    std::transform(str1.begin(), str1.end(), str1NoCase.begin(), tolower);
    std::transform(str2.begin(), str2.end(), str2NoCase.begin(), tolower);

    return (str1NoCase < str2NoCase);
  }
};

Now given

typedef std::map<std::string, std::string> DIRECTORY_WITHCASE;
typedef std::map<std::string, std::string, PredIgnoreCase> DIRECTORY_NOCASE;

I initialize

 // Case-sensitive directory: case of string-key plays no role
  DIRECTORY_WITHCASE dirCaseSensitive{
      make_pair("John", "2345764"),
      make_pair("JOHN", "2345765"),
      make_pair("Sara", "42367236"),
      make_pair("Jack", "32435348"),
  };

And then when I initialize

  DIRECTORY_NOCASE dirCaseInsensitive(dirCaseSensitive.begin(),
                                      dirCaseSensitive.end());

dirCaseInsensitive prints

Jack - >32435348
JOHN - >2345765 <---- John in upper case
Sara - >42367236

However, if I initialize the dirCaseInsensitive like:

DIRECTORY_NOCASE dirCaseInSensitive{
      make_pair("John", "2345764"),
      make_pair("JOHN", "2345765"),
      make_pair("Sara", "42367236"),
      make_pair("Jack", "32435348"),
  };

It outputs correct map:

Jack - >32435348
John - >2345764 <----- John in lower case
Sara - >42367236

Why would different constructors of the same map, give different results?

like image 791
Alex S. Avatar asked Mar 05 '23 09:03

Alex S.


2 Answers

The ordering matters here. If you switch make_pair("JOHN", "2345765") with make_pair("John", "2345764") in your dirCaseInSensitive construction, you will see the first output.

The case here is that when you first create:

DIRECTORY_WITHCASE dirCaseSensitive{
      make_pair("John", "2345764"),
      make_pair("JOHN", "2345765"),
      make_pair("Sara", "42367236"),
      make_pair("Jack", "32435348"),
};

due to ordering of the keys, your "JOHN" key is placed as the first key (before the "John" key). Now, if you try to use that very map to initialize the second one, it will first insert the ("JOHN", "2345765") pair and then see the ("John", "2345764") pair. It will case-insensitive compare the keys and figure out they are equivalent, so it won't insert the lowercase "John" pair.

So, to summarize, it's not really the usage of constructors. It's the surprising mix of your order of provided pairs and the ordering of they key, which do not match.

like image 195
Fureeish Avatar answered Mar 07 '23 23:03

Fureeish


Because the order of pairs in each case is different. In the first case the constructor of dirCaseInsensitive is called (in effect) with the pairs in this order.

  make_pair("JOHN", "2345765"),
  make_pair("Jack", "32435348"),
  make_pair("John", "2345764"),
  make_pair("Sara", "42367236"),

This is the order of the pairs in dirCaseSensitive (assuming ASCII or similar character set).

In the second case the constructor of dirCaseInsensitive is called with pairs in the order given by the initialiser list.

  make_pair("John", "2345764"),
  make_pair("JOHN", "2345765"),
  make_pair("Sara", "42367236"),
  make_pair("Jack", "32435348"),

So you can see in the first case "JOHN" is before "John" so "JOHN" gets inserted but in the second case it's the other way around.

like image 29
john Avatar answered Mar 07 '23 22:03

john