【问题标题】:Overload Resolution differs between compilers编译器之间的重载分辨率不同
【发布时间】:2020-05-31 22:04:29
【问题描述】:

我构建了以下问题的最小示例:

#include <iostream>

struct Foo {
  Foo() {
    std::cout << "default" << std::endl;
  }
  Foo(Foo& f2) {
    std::cout << "non-const" << std::endl;
  }
  Foo(const Foo& f2) {
    std::cout << "const" << std::endl;
  }
};

int main() {
        std::pair<Foo, int> foop0(Foo(), 1);
        std::cout << std::endl;
        std::pair<const Foo, int>foop1(foop0);
}

在我的 Ubuntu 机器上 g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0 将打印出以下内容:

$ g++ -std=c++14 test.cpp -o test && ./test
default
const

const

但是,Apple clang(版本 11.0.3 (clang-1103.0.32.62) Target: x86_64-apple-darwin19.4.0) 在我的 Mac 上将打印:

$ g++ -std=c++14 test.cpp -o test && ./test
default
const

non-const

但是,情况变得更糟:如果我将最后一行更改为

std::pair<Foo, int>foop1(foop0);
          ^ removed const

两个编译器都会给出第一个输出。

为什么会这样?

编辑:我现在明白了为什么,根据cppreference,std::pair 的 ctor 应该由 g++ 选择。仍然没有在这里解释 clang 的奇怪行为。可能是不合格的实现?

【问题讨论】:

  • 他们是否使用相同版本的 C 标准?如果添加开关来指定标准版本会发生什么?
  • clang 是什么版本?最新的version 打印 const。
  • @EricPostpischil 在主干上,所有 c++ 版本在 gcc 和 clang 上打印 const。至少从 c++11 开始。
  • 两者都是 C++14。我现在编辑了问题。
  • 你也可以运行--version 并为两个编译器添加该结果吗?

标签: c++ compilation overload-resolution


【解决方案1】:

就像已经说过的那样,std::pair 的实现可能两者都不同。我写了两个非常相似的对的实现,它们完全展示了你不同的行为,即使没有改变对的类型:godbolt

#include <iostream>

struct T {
    T() { 
        std::cerr << "default\n";
    }

    T(T&) {
        std::cerr << "non-const\n";
    }

    T(const T&) {
        std::cerr << "const\n";
    }

};

// Comment or uncomment to change the behavior.
//#define MAC

template<class First, class Second>
struct pair {
    First first;
    Second second;

    pair(const First& f, const Second& s) : first(f), second(s) {
    }

#ifdef MAC

    pair(pair<First, Second>& p) : first(p.first), second(p.second) {
        std::cerr << "copy Mac-Like\n";
    }

#else

    pair( pair<First, Second>& p) : pair(p.first, p.second) {
        std::cerr << "copy Ubuntu-Like\n";
    }
#endif

};

int main() {
    T t;
    pair<T, int> u1(t, 0);

    pair<T, int> u2(u1);
}

当然,mac 和 ubuntu 上的对都写得更合理(并且符合标准)并且标准复制构造函数采用 const 引用(这就是它们都使用 const 变体的原因)。但我猜他们以不同的方式处理具有不同但可转换类型的对中的复制构造函数。找出到底有什么不同需要比较两个系统上的 stl 实现。

Ubuntu 变体对我来说似乎很清楚,这对只是通过构造函数中的 const 引用从一对可转换类型中获取的。当你在你的构造链的任何一点都有一个 const 时,你最终会得到 T 的 const 复制构造函数。

我发现 Mac 的行为有点奇怪,因为它们必须通过值或非 const 引用来获取对(实际上,你不应该有一个复制构造函数通过非 const 引用来获取,为什么它应该改变它复制的东西?这看起来像std::auto_ptr-level 怪异)。也许他们(试图)在某种“按价值接受然后移动”的事情上很聪明。

但我认为这是不一致的,因为 pair constructor 应该通过 const-reference 或 rvalue-reference 获取所有其他对。由于我们正在复制,它应该使用复制构造函数,获取一个 const 引用,因此也有一个对 pair.first 的 const 引用,并由此获取它的 const 复制构造函数。

【讨论】:

  • 这和构造函数匹配问题有关,因为他不能用非常量对象创建复制构造函数,如果他用常量对象注释复制构造函数的定义编译器会产生错误,所以std::pairs没有什么可以解决这个问题(如果您认为,请详细说明)。
  • 看看你得到的错误,当你删除 const 复制构造函数时。此问题出现两次。首先,因为您无法将非常量引用绑定到由 OP 的第一个对构造函数中的 Foo() 创建的临时引用。如果您解决此问题(例如通过预先创建命名对象),则问题仅存在于第二对构造函数中。您将看到问题来自于构造函数 std::pair(const std::pair&amp;) 被调用,该构造函数稍后需要 Foo 的 const 复制构造函数。我想(但无法测试)mac上不会有问题。
  • 你可能是对的,我已经发布了一个问题目标这一点我已经在这里发布了我的想法链接,谢谢。 stackoverflow.com/questions/62128493/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
相关资源
最近更新 更多