首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >clang和gcc在用户定义转换算子过载选择上的差异

clang和gcc在用户定义转换算子过载选择上的差异
EN

Stack Overflow用户
提问于 2020-08-27 15:05:28
回答 1查看 135关注 0票数 4

当我试图了解与这个问题相关的编译器行为(gcc和clang)时,我只是不明白为何gcc和clang之间在第三个案例(见下文)中存在差异。问题是而不是关于这种转换API的正确性(特别是参考案例)。

请您帮助我理解这个场景中预期的行为(从c++标准的角度来看)吗?

编辑:正如注释中所述,这种行为只能从-std=c++17中观察到。在此之前,引用转换是使用gcc的。

EDIT2:注意正确的行为“似乎”是gcc,因为隐式this参数不是const,因此非const重载是首选的。

下面是示例代码:

代码语言:javascript
复制
struct SInternal {
    SInternal() = default;
    SInternal(const SInternal&) {
        std::cout << "copy ctor" << std::endl;
    }
    int uuid{0};
};

struct S {
 SInternal s;

 S() = default;

 operator SInternal() const {
     std::cout << "copy conversion" << std::endl;
     return s;
 }

 operator SInternal& () {
     std::cout << "ref conversion" << std::endl;
     return s;
 }
};

int main() {
    S s;
    const S s2;
    // 1-
    //SInternal si = s; // no ambiguity, ref conversion
    //SInternal si = s2; // no ambiguity, copy conversion
    // 2-
    // SInternal& si = s; // no ambiguity, ref conversion
    // SInternal& si = s2; // no viable conversion operator SInternal& not const
    // Case 3- WHAT IS THE CORRECT EXPECTED BEHAVIOR HERE?
    SInternal si(s); // no ambiguity but clang uses copy conversion
                     // while gcc uses ref conversion
    //SInternal si(s2); // no ambiguity, copy conversion
    // 4-
    //SInternal si = std::move(s); // no ambiguity ref conversion

    std::cout << "test " << si.uuid << std::endl;
}

这里的演示

谢谢你的帮助。

EN

回答 1

Stack Overflow用户

发布于 2020-08-28 12:44:01

以下是我从目前为止的研究和评论中得到的善意帮助,试图回答我自己的问题。

评论中的任何意见都是非常欢迎的,以改进答案。

答案与这个答案就是那个密切相关,因此问题本身可能是重复的。

gcc案

  1. 这是直接初始化(SInternal si(s);)的情况。这个案件属于dcl.init

否则,如果初始化是直接初始化,或者是复制初始化,源类型的cv非限定版本与目标类或目标类的派生类相同,则考虑构造函数。列举了适用的构造函数(over.match.ctor),并通过过载解析选择了最优的构造函数。这样选择的构造函数被调用来初始化对象,初始化器表达式或表达式列表作为它的参数。如果不应用构造函数,或者重载解析不明确,则初始化格式不正确.

结果:考虑了SInternal()SInternal(const SInternal&),并选择了SInternal(const SInternal&)

  1. 引用必须绑定到SInternal (SInternal是正在初始化的引用的类型,S是初始化表达式的类型)。本案属于over.match.ref: ..。考虑了S及其基类的转换函数。在S中不隐藏的非显式转换函数和屈服类型“cv2 T2的lvalue引用”(初始化lvalue引用或对函数的rvalue引用时)或“cv2 T2”或“对cv2 T2的rvalue引用”(当初始化rvalue引用或对函数的lvalue引用时),其中“cv1 T”是与“cv2 T2”兼容的引用(dcl.init.ref),是候选函数.

结果:候选函数是operator SInternal() constoperator SInternal& ()

  1. 过载解析。这个案件属于over.match.ref

结果:选择operator SInternal& ()是因为隐式this参数是非const的.

  1. 最终结果伪码: SInternal(operator SInternal& ())

clang案

就像在其他文章中解释的那样,这种行为似乎与CWG 2327有关。

如果这是这种行为的编译器实现,则考虑直接初始化转换函数,并选择operator SInternal () const

最后一点是转换操作符的实现。如果SInternal复制ctor变得微不足道,则不会调用复制构造函数。如果定义了空副本构造函数,则调用它(演示)。

这是因为SInternal变成了TriviallyCopyable,因此编译器利用它来为副本使用寄存器。如果您用更多的数据成员(例如,SInternal )填充char arr[32];,它将最终使用memcpy

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/63618919

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档