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

C++17:显式转换函数vs显式构造函数+隐式转换-规则改变了吗?

吴高远
2023-03-14

CLANG6、CLANG7和gcc 7.1、7.2和7.3都同意以下代码是有效的C++17代码,但在C++14和C++11下有歧义。MSVC2015和2017也接受它。然而,即使在C++17模式下,GCC-8.1和8.2也拒绝了它:

struct Foo
{
    explicit Foo(int ptr);
};

template<class T>
struct Bar
{
    operator T() const;
    template<typename T2>
    explicit operator T2() const;
};


Foo foo(Bar<char> x)
{
    return (Foo)x;
}

接受它的编译器选择模板化的显式转换函数bar::operator T2()

拒绝它的编译器同意在以下两个方面存在歧义:

    null
<source>: In function 'Foo foo(Bar<char>)':    
<source>:17:17: error: call of overloaded 'Foo(Bar<char>&)' is ambiguous    
     return (Foo)x;    
                 ^    
<source>:3:14: note: candidate: 'Foo::Foo(int)'    
     explicit Foo(int ptr);    
              ^~~    
<source>:1:8: note: candidate: 'constexpr Foo::Foo(const Foo&)'    
 struct Foo    
        ^~~    
<source>:1:8: note: candidate: 'constexpr Foo::Foo(Foo&&)'

以下是来自clang-7-std=c++14(clang-7-std=c++17接受代码)的错误:

<source>:17:12: error: ambiguous conversion for C-style cast from 'Bar<char>' to 'Foo'    
    return (Foo)x;    
           ^~~~~~    
<source>:1:8: note: candidate constructor (the implicit move constructor)    
struct Foo    
       ^    
<source>:1:8: note: candidate constructor (the implicit copy constructor)    
<source>:3:14: note: candidate constructor    
    explicit Foo(int ptr);    
             ^    
1 error generated.

共有1个答案

邢浩邈
2023-03-14

这里有几种力量在起作用。为了理解发生了什么,让我们研究一下(Foo)x应该引导我们去哪里。首先,在这种特殊情况下,C样式的强制转换等效于static_cast。静态强制转换的语义是直接初始化结果对象。由于结果对象属于类类型,[dcl.init]/17.6.2告诉我们它的初始化如下:

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

因此重载解析来选择要调用的foo的构造函数。如果重载解析失败,则程序格式不正确。在这种情况下,它不应该失败,即使我们有3个候选构造函数。它们是Foo(int)Foo(Fooconst&)Foo(Foo&&)

  • 考虑了S及其基类的转换函数。那些不隐藏在S中并产生类型“对cv2 t2的lvalue引用”(当初始化对函数的lvalue引用或rvalue引用时)或“对cv2 t2的rvalue引用”(当初始化对函数的rvalue引用或lvalue引用时)的非显式转换函数是候选函数,其中“CV1 t”与“cv2 t2”是引用兼容的([dcl.init.ref])。对于直接初始化,那些不隐藏在S中的显式转换函数也是候选函数,它们分别产生类型为“lvalue reference to cv2 T2”或“cv2 T2”或“rvalue reference to cv2 T2”,其中T2是与T相同的类型,或者可以通过限定转换([conv.qual])转换为T类型。

唯一可以生成foo类型的glvalue或prvalue的转换函数是指定的显式转换函数模板的专门化。但是,由于函数参数的初始化不是直接初始化,我们不能考虑显式转换函数。因此,我们不能在重载解析中调用复制或移动构造函数。这就只剩下构造函数接受int了。所以重载解析是成功的,应该就是这样。

那么为什么有些编译器会发现它不明确,或者转而调用模板化的转换运算符呢?好吧,自从保证拷贝省略被引入到标准中以来,人们注意到(CWG问题2327),用户定义的转换函数也应该有助于拷贝省略。今天,根据干信的标准,他们没有。但我们真的很希望他们。虽然具体应该如何完成的措辞仍在制定中,但似乎一些编译器已经开始尝试实现它。

 类似资料:
  • 主要内容:转换构造函数,再谈构造函数,对 Complex 类的进一步精简在 C/ C++ 中,不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换。 自动类型转换示例: 编译器对 7.5 是作为 double 类型处理的,在求解表达式时,先将 a 转换为 double 类型,然后与 7.5 相加,得到和为 13.5。在向整型变量 a 赋值时,将 13.5 转换为整数 13,然后赋给 a。整

  • 我试图理解以下转换的机制 根据整数常量,的类型是。这可以通过执行轻松检查; 根据内隐转换语义学: “整数提升是对秩小于或等于int[…]秩的任何整数类型的值的隐式转换设置为int或unsigned int类型的值。“ 如下文所述 msgstr"所有有符号整数类型的行列等于相应无符号整数类型的行列" 因此,升级适用,因为的秩与的秩相同。 这种晋升被定义为 如果int可以表示原始类型的整个值范围(或原

  • 我有一个下面的例子(布尔类型过于安全): 众所周知,这样的代码包含一个错误:“无法转换’((int)((const A*)this)- 我想说: 由于指定的障碍,我不能简单地将的所有条目替换为我的用户代码中的超级安全的(这是mocked-对象),因为,例如,在使用了上述构造,将其视为隐式转换。类似的障碍并不唯一。

  • 问题内容: “隐式转换”和“显式转换”有什么区别?Java和C ++的区别是否不同? 问题答案: 显式转换是您使用某种语法 告诉 程序进行转换的地方。例如(在Java中): 隐式转换是在没有任何语法的情况下进行转换的地方。例如(在Java中): 应该注意的是(在Java中)涉及原始类型的转换通常涉及某种表示形式的更改,并且可能导致精度降低或信息丢失。相比之下,仅涉及引用类型的转换不会更改基本表示形

  • 以下代码在C++11中编译成功: 它包括用于现代C++的JSON。一个工作示例在这个Wandbox中。 JSON变量被隐式转换为字符串。但是,如果我取消最后一行的注释,这是一个显式转换,它将无法编译。这里的编译结果。 除了这个json库的特殊细微差别之外,如何编写一个类,使隐式转换工作,而显式转换不工作? 是否有某种构造函数限定符允许这种行为?