当前位置: 首页 > 知识库问答 >
问题:

C 'std::set_difference' 为不同的编译器提供不同的输出

岳阳文
2023-03-14

现在,我有下面的代码,它只是测试标准库中的< code>std::set_difference:

#include <algorithm>
#include <vector>
#include <memory>
#include <iostream>

struct Node{
    public:
        Node(int x) : foo(x){}
    int foo = 10;
};

using NodePtrVec = std::vector<std::shared_ptr<Node>>;
using NodePtr = std::shared_ptr<Node>;
using IntVec = std::vector<int>;
int main(void){

    // pointer test
    NodePtr inA_1 = std::make_shared<Node>(1);
    NodePtr inA_2 = std::make_shared<Node>(11);
    NodePtr inA_3 = std::make_shared<Node>(111);

    NodePtr inB_1 = std::make_shared<Node>(2);
    NodePtr inB_2 = std::make_shared<Node>(22);
    NodePtr inB_3 = std::make_shared<Node>(222);

    NodePtr both_1 = std::make_shared<Node>(3);
    NodePtr both_2 = std::make_shared<Node>(33);


    NodePtrVec a{inA_1,inA_2,inA_3,both_1,both_2};
    NodePtrVec b{inB_1,inB_2,inB_3,both_1,both_2};
    NodePtrVec c{};
    std::set_difference(a.begin(), a.end(), b.begin(), b.end(),
                        std::back_inserter(c));

    for(const auto& tmp : c){
        std::cout << tmp->foo << std::endl;
    }
    // int test
    std::cout << "int test" << std::endl;
    IntVec int_a = {1,5,4,2,3};
    IntVec int_b = {1,10,7};
    IntVec int_c;
    std::set_difference(int_a.begin(), int_a.end(), int_b.begin(), int_b.end(),
                        std::back_inserter(int_c));

    for(const auto& tmp : int_c){
        std::cout << tmp << std::endl;
    }
}

当我用Clang/GCC编译它时,我得到了输出:

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
1
11
111
int test
5
4
2
3

更新我想实际使用的源代码案例(假设Node是我将要做的一些操作。因此,这些操作将按总数的顺序发生):

#include <algorithm>
#include <vector>
#include <memory>
#include <iostream>

struct Node{
    public:
        Node(int x) : foo(x){}
    int foo = 10;
};

using NodePtrVec = std::vector<std::shared_ptr<Node>>;
using NodePtr = std::shared_ptr<Node>;
using IntVec = std::vector<int>;
int main(void){

    // pointer test
    NodePtr inA_1 = std::make_shared<Node>(1);
    NodePtr inA_2 = std::make_shared<Node>(11);
    NodePtr inA_3 = std::make_shared<Node>(111);

    // total elements
    NodePtrVec total{inA_1,inA_2,inA_3};
    // sub set of the elements
    NodePtrVec sub{inA_2};
    NodePtrVec c{};
    // just want to get the compliment of the sub in total
    // as expected, c should be {inA_1,inA_3}
    std::set_difference(total.begin(), total.end(), sub.begin(), sub.end(),
                        std::back_inserter(c));

    for(const auto& tmp : c){
        std::cout << tmp->foo << std::endl;
    }
}

好吧,这看起来不错,它实际上对应于标准::set_difference,检查链接:但是当我选择MSVC时,我得到了不同的输出(检查链接:https://godbolt.org/z/nYre1Eono):

example.cpp
ASM generation compiler returned: 0
example.cpp
Execution build compiler returned: 0
Program returned: 0
1
11
111
3
33
int test
5
4
2
3

哎呀,我们得到了意想不到的结果!顺便说一下,当我更改MSVC编译器版本时,输出似乎会改变。但是为什么呢?

共有2个答案

狄望
2023-03-14

就像注释中提到的,在你的例子中< code>std::set_difference比较的是< code>shared_ptr,而不是指针指向的值。当然,它仍然可以工作,因为< code>std::shared_ptr有< code>operator==只比较原始指针的地址,但是set_difference的要求是对范围进行排序,如果您查看MSVC的输出,情况并非如此(看起来像是碰巧按升序分配了内存,这导致了排序的向量)。首先对向量进行排序:

std::sort(a.begin(), a.end());
std::sort(b.begin(), b.end());

在MSVC,你也会得到你想要的结果。或者更好的是,使用自定义比较器对向量进行排序,然后使用自定义比较器计算集合差,这样会更习惯:

std::sort(a.begin(), a.end(), [](auto lhs, auto rhs) { return lhs->foo < rhs->foo; });
std::sort(b.begin(), b.end(), [](auto lhs, auto rhs) { return lhs->foo < rhs->foo; });
std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::back_inserter(c), [](auto lhs, auto rhs) {
    return lhs->foo < rhs->foo;
});

编辑

我刚刚在评论中读到,你实际上想比较指针,而不是它们指向的值。在这种情况下,我的答案的第一部分是最相关的。

编辑2:

所以这些行为会按照总数的顺序发生

然后,您必须复制矢量,对其进行排序并计算差值,或者您不能在您的情况下使用std::set_difference。如果需要保留操作的相对顺序,可以尝试以下操作:迭代 A 并将元素插入到 C 中(如果 B 中不存在)。由于只需要保留 A 中元素的顺序,因此对容器 B 使用 std::unordered_set,因为它具有常量时间查找(平均):

NodePtrVec total{inA_1,inA_2,inA_3};
std::unordered_set<NodePtr> sub{inA_2};
NodePtrVec c{};
for (auto el : total)
{
    if (sub.count(el) == 0)
    {
        c.push_back(el);
    }
}
弘浩瀚
2023-03-14

必须对输入到< code>set_difference的范围进行排序。你的没有分类。

 类似资料:
  • 我正在尝试实现ESAPI编码以防止在我的spring-mvc项目中进行XSS攻击。我的方法是在发送回响应中的输入之前,我将使用 ESAPI.encoder() 对其进行编码,以便在页面响应中对输入属性进行编码。 我的假设是,当我返回编码的响应时,在我的页面响应中,我将得到编码的字符,而在我的输入字段中,我会得到普通的字符,但我的输入域也设置了编码的字符。 下面是我在发送响应之前在控制器中编写的示例

  • 问题内容: 给定相同的主要版本,例如Java 7,不同的Java编译器(例如Oracle的热点,JRockit或IBM的J9等)是否将给定的Java源代码文件编译为相同的bytcode? 扫描Java 7语言规范 ,似乎正在讨论的是语言的语义,而不是代码到字节码的转换。 YES 。 以上摘录为: JLS留下了许多实现细节,因一个实现而异。 和 但是,JLS没有指定从源代码到生成的字节码的1:1映射

  • 我将json传递给ObjectMapper。JSON字符串如下所示: 我的类如下所示: 这种行为是意料之中的吗?如果是,有什么解决办法? 更新:添加了类描述。

  • 考虑下面这个简短的C++程序: 如果我在不同的编译器上编译它,我会得到不同的结果。对于CLANG3.4和GCC 4.4.7,它打印,而Visual Studio 2013打印,这意味着它们在调用不同的强制转换操作符。根据标准,哪一个是正确的行为? 根据我的理解,不需要转换,而需要到的转换,因此编译器应该选择第一个。对此做了什么吗?const-conversion是否被编译器认为更“昂贵”? 如果删

  • 上面的代码使用不同的编译器会有不同的结果。这是编译器的错误还是我漏掉了什么? 叮叮当当 1 1 (https://godbolt.org/z/s43T55rxq) msvc 1 1 (https://godbolt.org/z/YnKfKh41q) 全球循环 0 1 (https://godbolt.org/z/91xdfv93c)

  • 问题内容: 我需要获取UNIX时间戳(字符串),将其转换为特定格式的日期,并将其存储在DATETIME列的MySQL数据库中。 这是我的代码(简化): 控制台输出: 当我尝试在MySQL数据库中推送此值时,它将引发Invalid DateError。 只是感到困惑,并以不同的格式显示时间戳。 问题答案: 不要将日期作为字符串传递给MySQL数据库。更好,更容易,更安全地传递日期对象。一方面,它可以