当我试图了解与这个问题相关的编译器行为(gcc和clang)时,我只是不明白为何gcc和clang之间在第三个案例(见下文)中存在差异。问题是而不是关于这种转换API的正确性(特别是参考案例)。
请您帮助我理解这个场景中预期的行为(从c++标准的角度来看)吗?
编辑:正如注释中所述,这种行为只能从-std=c++17中观察到。在此之前,引用转换是使用gcc的。
EDIT2:注意正确的行为“似乎”是gcc,因为隐式this参数不是const,因此非const重载是首选的。
下面是示例代码:
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;
}谢谢你的帮助。
发布于 2020-08-28 12:44:01
以下是我从目前为止的研究和评论中得到的善意帮助,试图回答我自己的问题。
评论中的任何意见都是非常欢迎的,以改进答案。
答案与这个答案和就是那个密切相关,因此问题本身可能是重复的。
gcc案
SInternal si(s);)的情况。这个案件属于dcl.init否则,如果初始化是直接初始化,或者是复制初始化,源类型的cv非限定版本与目标类或目标类的派生类相同,则考虑构造函数。列举了适用的构造函数(over.match.ctor),并通过过载解析选择了最优的构造函数。这样选择的构造函数被调用来初始化对象,初始化器表达式或表达式列表作为它的参数。如果不应用构造函数,或者重载解析不明确,则初始化格式不正确.
结果:考虑了SInternal()和SInternal(const SInternal&),并选择了SInternal(const 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() const和operator SInternal& ()
结果:选择operator SInternal& ()是因为隐式this参数是非const的.
SInternal(operator SInternal& ())clang案
就像在其他文章中解释的那样,这种行为似乎与CWG 2327有关。
如果这是这种行为的编译器实现,则考虑直接初始化转换函数,并选择operator SInternal () const。
最后一点是转换操作符的实现。如果SInternal复制ctor变得微不足道,则不会调用复制构造函数。如果定义了空副本构造函数,则调用它(演示)。
这是因为SInternal变成了TriviallyCopyable,因此编译器利用它来为副本使用寄存器。如果您用更多的数据成员(例如,SInternal )填充char arr[32];,它将最终使用memcpy。
https://stackoverflow.com/questions/63618919
复制相似问题