3

For an unordered_set using a custom equality function, that equality definition is ignored when two sets are compared. Example:

#include <iostream>
#include <unordered_set>
#include <string>

struct Hash {
    size_t operator()(const std::string& str) const {
        return 0;
    }
};

struct InsensitiveCompare { 
    bool operator() (const std::string& str1, const std::string& str2) const {
        // https://stackoverflow.com/a/43226907/2204581
        return str1.size() == str2.size() && std::equal(str1.begin(), str1.end(), str2.begin(), [](auto a, auto b){return std::tolower(a)==std::tolower(b);});
    }
};

int main()
{
    std::unordered_set<std::string, Hash, InsensitiveCompare> s1, s2;
    
    s1.emplace("test");
    s2.emplace("Test");
    
    std::cout << (s1 == s2 ? "" : "not ") << "equal" << std::endl;
    // prints "not equal"
    
    return 0;
}

http://cpp.sh/2vmwc5

I do not understand why the custom equality function is not forwarded to std::is_permutation. operator== requires the comparator to have the same behavior on the left and right side, so either comparator could be used. If you ask me, this behavior is completely unexpected in the best case and wrong in the worst. However, this is unlikely to change.

My question is if there is any type of static code analysis/linting tool that would spot this. Neither -Weverything nor clang-tidy let me know about this.

mrks
  • 8,033
  • 1
  • 33
  • 62
  • 1
    Your desired semantics is impossible. Comparators are not classes, they are *objects*. Two different sets may have two different comparators. Which one do you propose to pass to `is_permutation`? – n. m. could be an AI Aug 20 '20 at 09:07
  • Good point. I don't think I ever explicitly passed a stateful comparator object, so I didn't consider that part. Looks like a solution to the underlying problem is not as easy as I thought. The main question remains. – mrks Aug 20 '20 at 10:02
  • On the second thought, if the comparators do not have the same behaviour for both containers, the behaviour is undefined, so ostensibly it could pass either one. Anyway, you can wrap your data into a thin wrapper class that redefines equality. – n. m. could be an AI Aug 20 '20 at 10:22

0 Answers0