插入 std::set<std::tuple<std::string, ...>> 时重复

Posted

技术标签:

【中文标题】插入 std::set<std::tuple<std::string, ...>> 时重复【英文标题】:Duplicates when inserting into std::set<std::tuple<std::string, ...>> 【发布时间】:2021-03-02 11:17:25 【问题描述】:

我尝试使用 std::tuple 作为 std::set 的一个元素,其中 std 元组由 std::string 和一个 bool 组成(仅用于此示例,在正常使用情况下,元组由 3 个元素组成)。元组的字符串应该是唯一的,因此我创建了比较器,它将对元组的第一个元素上的元素进行排序。但是,当插入上述集合时,我确实在容器中得到了重复的元素。我看不出我在哪里犯了错误。提前感谢您的建议!

#include <set>
#include <functional>
#include <iostream>

using namespace std;

int main() 

    using tuple_t = std::tuple<std::string, bool>;
    std::function<bool(const tuple_t &, const tuple_t &)> tupleCmp =
            [](const tuple_t & l, const tuple_t & r) -> bool 
                return static_cast<bool>(std::get<0>(l).compare(std::get<0>(r)));
            ;

    set<tuple_t, decltype(tupleCmp)> s(tupleCmp);
    s.insert(make_tuple("ahoy", false));
    s.insert(make_tuple("this", false));
    s.insert(make_tuple("creates", false));
    s.insert(make_tuple("duplicates", false));
    s.insert(make_tuple("ahoy", false));

    for (const auto & i :  s)
        cout << get<0>(i) << endl;
    
    auto debug_stop = 0;

Debugger snippet

Output:
ahoy
duplicates
creates
this
ahoy

【问题讨论】:

我认为您的代码具有未定义的行为,因为tupleCmp 没有按照compare 的要求创建严格的弱排序。 std::set 的比较器不应该检查是否相等,而是暗示顺序。它的返回值必须指明是否为l &lt; r 谢谢@churill,我一定误解了集合构造函数文档的“comp”。 return static_cast&lt;bool&gt;(std::get&lt;0&gt;(l)&lt;(std::get&lt;0&gt;(r))); 工作。 【参考方案1】:

std::string::compare 返回int(负,0,正)而不是bool(小于),

你想要的

std::get<0>(l).compare(std::get<0>(r)) < 0;

std::get<0>(l) < std::get<0>(r);

【讨论】:

【参考方案2】:

a.compare(b) 返回:

0          when the strings are equal
negative   when a comes before b  (ie a < b)
positive   when b comes before a  (ie b < a)

但是,比较器的返回值被转换为bool,除0 之外的任何值都被转换为true,因此您的比较器认为a &lt; b 对于任何ab 都是true除非a == b。特别是a &lt; btrue 并且b &lt; a 也被认为是true。这不符合strict weak ordering 的要求,并导致未定义的行为。您可以使用“普通”&lt; 来比较字符串。

【讨论】:

嗯,我的知识有差距。在测试它之前,我发誓 static_cast(-1) 会产生错误(正如你指出的那样,它会产生真)。 en.cppreference.com/w/cpp/language/implicit_conversion。 (布尔转换部分) @Godzilla 认为 std::string::compare 模仿 Cs strcmp 并且在早期 C 没有 bool。我猜如果 C++ 从一开始就进行三向比较std::string::compare 将不存在

以上是关于插入 std::set<std::tuple<std::string, ...>> 时重复的主要内容,如果未能解决你的问题,请参考以下文章

std::set 插入器不尊重自定义比较器。 (可能的编译器错误?)

std::get(std::tuple)

std::tuple

将 std::vector<std::tuple<>> 转换为 torch::Tensor 的最有效方法是啥?

在std :: tuple中转换元素

创建 std::promise<std::tuple<T>> 时出现错误 C2512(仅限 Visual Studio)