容器比较器使用虚拟实例

时间:2018-10-17 06:36:33

标签: c++ std

我今天偶然发现了一件非常微妙的事情。这是一些代码来演示:

final String searchURL = "https://www.amazon.com/s/?field-keywords=";
String keywords = "something";
view.loadUrl(searchURL + keywords);

输出为:

#include <set>
#include <iostream>

class MyClass {
private:
    int id = -1;
public:
    MyClass() {}
    MyClass(int _id) : id(_id) {}

    bool operator() (const MyClass& instance1, const MyClass& instance2) const {
        std::cout << id << std::endl;
        std::cout << instance1.id << std::endl;
        std::cout << instance2.id << std::endl;

        return true;
    }

};

int main(int argc, char *argv[])
{
    std::set<MyClass, MyClass> classSet;
    classSet.insert(MyClass(1));
    classSet.insert(MyClass(2));

    return 0;
}

当我使用设置-1 2 1 值的专用构造函数创建所有类实例时,我很惊讶id的值为-1。显然,C ++使用标准构造函数创建了一些虚拟实例。

我的问题是:

  • 是否有一些有关此行为的文档?
  • 我是否违反了某些原则,例如此处的“三定律”。复制构造函数会“解决”问题(复制实例之一,因此this->id是1还是2)?还是我总是必须在内部使用标准构造函数为C ++准备我的类?

2 个答案:

答案 0 :(得分:5)

编写std::set<MyClass, MyClass>时,它声明需要使用类MyClass的实例来比较集合中包含的每两个实例。

如果std::set的比较器是默认可构造的,则该集合将创建一个实例进行比较(出于明显的原因)。它将使用您提供的默认构造函数,该构造函数会将id设置为您提供的默认成员初始化程序。

如果要删除默认的构造函数,则该程序将不会生成,因为std::set将要求您在设置对象初始化时提供一个比较器实例。

答案 1 :(得分:4)

您的比较器函数是一个非静态成员函数,这意味着它需要一个对象被调用。 std::set会创建这样的实例,因为您可以通过提供MyClass类型作为模板参数来平铺它。

我的建议是改用非成员operator<函数重载。

class MyClass {
public:
    ...

    // the `friend` keyword makes this function a non-member function
    friend bool operator< (const MyClass& instance1, const MyClass& instance2) {
        std::cout << instance1.id << std::endl;
        std::cout << instance2.id << std::endl;

        return instance1.id < instance2.id;
    }
};

...

// No comparator type needed
std::set<MyClass> classSet;