c++visual-c++type-conversionimplicit-conversionstandards-compliance

Multiple user-defined conversions on initialization


I am aware of the fact that C++ allows only a single user-defined implicit conversion when converting between types. However, I recently came across a situation where it seems like double user-defined implicit conversions are allowed on initialization.

Consider the following classes:

//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.
};

As expected, the following will not compile:

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
}

However, the following does compiles in Visual Studio 2013:

int main() {
    RingExtension<Rational, 2> x = 3; // int -> Rational -> RingExtension<Rational, 2>
}

Why is the double user-defined conversion allowed in the latter situation? Is Visual Studio not standard-compliant in this particular situation? Or is there some exception in the standard for initialization?

EDIT: As Kerrek SB suggested, I have tested my code in clang and gcc through Wandbox. Both compilers spat out an error on the initialization, as one would expect. Question remains, who is in the wrong here? It would seem that Visual Studio is too permissive, but it would be great if someone could confirm this.

EDIT: The full source code for these classes, including the main()-function, can be found here: http://pastebin.com/JNSvkwi0#


Solution

  • That seems to be a non-standard Microsoft extension that can be disabled by passing /permissive- or /std:c++20 (that enables /permissive- by default). I was unable to find a narrower /Zc: option. I was also unable to find Microsoft documentation about this.

    However, there is a corresponding JetBrains ReSharper warning even back in their version 2017.2:

    More than one implicit conversion applied during copy-initialization. This is non-standard Microsoft C++ extension

    Unfortunately, the link to connect.microsoft.com from the comments is now dead and I was unable to find corresponding topic at visualstudio.com. So I've added all other links from this answer to https://web.archive.org/ as a precaution.