Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Chaining user conversions in c++

Tags:

c++

Consider the following piece of code:

#include <iostream>

struct C
{
    C() 
    {   
        std::cout << "C()\n";
    }   

    explicit C(const C&) 
    {   
        std::cout << "C(const C&)\n";
    }   

    operator int()
    {   
        std::cout << "int()\n";
        return 1;
    }   

    C(int)
    {   
        std::cout << "C(int)\n";
    }   
};

int main()
{
    C c1; 
    std::cout << '\n';
    C c2 = c1; 
    std::cout << '\n';
    C c3(c1);
}

Both g++ and clang give the following output:

C()

int()
C(int)

C(const C&)

Doesn't it break the rule saying that an implicit conversion sequence can consist of at most one user conversion?

like image 831
piotrekg2 Avatar asked Apr 18 '15 21:04

piotrekg2


1 Answers

This only compiles because you're initializing class C with itself in the line C c2 = c1;. If you had a class D which behaves identically to C and tried D d; C c = d;, it wouldn't compile for the reason you stated: because the implicit conversion would require two user-defined conversions. Demonstation

The reason it compiles when using the same class is that copy-initialization (A x = y;) behaves differently when y is of type A or derived from it. In that case, a converting constructor is selected and that constructor is then invoked with the argument y, which may cause an implicit conversion. The constructor call itself is not part of the implicit conversion.

So in your code, the conversion sequence only contains one user-defined conversion: C to int, as the constructor C(int) is called separately.

See C++14 8.5/17:

  • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s).
  • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated [...]

Read more at http://en.cppreference.com/w/cpp/language/copy_initialization.

like image 192
interjay Avatar answered Oct 23 '22 16:10

interjay