初始化时多个用户定义的转换

JSQ*_*reD 5 c++ standards-compliance type-conversion visual-c++ implicit-conversion

我知道 C++ 在类型之间转换时只允许单个用户定义的隐式转换。然而,我最近遇到了一种情况,似乎在初始化时允许双重用户定义的隐式转换

考虑以下类:

//fractions
class Rational {
public:
    int num, den;
    // default constructor, etc.
    Rational(int n) : num(n), den(1) {} // NOT explicit
    // arithmetic and compound assignment defined between two Rational's.
};

//numbers of the form a + b sqrt(N), where a, b are of type R
template<typename R, int N>
class RingExtension {
public:
    R a, b;
    // default constructor, etc.
    RingExtension<R, N>(R a) : a(a), b(0) {} // NOT explicit
    // arithmetic and compound assignment defined between two RingExtension<R, N>'s.
};
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,以下内容将无法编译:

int main() {
    RingExtension<Rational, 2> x;
    x /= 3; // ERROR! Can't do the conversion int -> Rational -> RingExtension<Rational, 2>
    x /= (Rational)3; // this does work
}
Run Code Online (Sandbox Code Playgroud)

但是,以下内容可以在 Visual Studio 2013 中编译:

int main() {
    RingExtension<Rational, 2> x = 3; // int -> Rational -> RingExtension<Rational, 2>
}
Run Code Online (Sandbox Code Playgroud)

为什么在后一种情况下允许双重用户定义转换?在这种特殊情况下,Visual Studio 是否不符合标准?或者初始化标准中有一些例外吗?

编辑:正如 Kerrek SB 建议的那样,我已经通过 Wandbox 在 clang 和 gcc 中测试了我的代码。正如人们所期望的那样,两个编译器都会在初始化时抛出错误。问题仍然存在,谁错了?Visual Studio 似乎过于宽松,但如果有人能证实这一点那就太好了。

编辑:这些类的完整源代码,包括 main() 函数,可以在这里找到: http: //pastebin.com/JNSvkwi0#

yep*_*ons 2

这似乎是一个非标准的 Microsoft 扩展,可以通过传递/permissive-or来禁用/std:c++20/permissive-默认情况下启用)。我无法找到更窄的/Zc:选择。我也无法找到有关此问题的 Microsoft 文档。

然而,甚至早在2017.2 版本中就有相应的 JetBrains ReSharper 警告:

复制初始化期间应用了不止一种隐式转换。这是非标准的 Microsoft C++ 扩展

不幸的是,评论中指向 connect.microsoft.com 的链接现已失效,我无法在 Visualstudio.com 上找到相应的主题。因此,作为预防措施,我已将此答案中的所有其他链接添加到https://web.archive.org/ 。