c++ explicit constructor called in implicit situation

120 views Asked by At

I compiled the code below using g++ 6.3.0, with -std=c++14 option.

#include <utility>
#include <iostream>
struct A{
    int x;
    A(const A&)=default;
    A(int x):x(x){}
};

struct B{
  A a;
  template<class... Args>
  B(Args&&... args):a(std::forward<Args>(args)...){
    std::cout<<"1!"<<std::endl;
  }

  explicit B(const A& a):a(a){std::cout<<"2!"<<std::endl;}
};
struct C:B{
  using B::B;
};
int main(){
    A a{2};
    const A& aref=a;
    C c=aref; //Implicit conversion
}

I expected it to output "1!" since the conversion is implicit, but it output "2!". If I comment out the template constructor, it will not compile. Is this the correct behavior, or is this some kind of g++ bug?

1

There are 1 answers

0
Fedor On

Yes, your program shall print 1!.

A simplified version of the program showing compiler divergence is

struct B {
    int b;
    constexpr B(auto) { b = 1; }
    constexpr explicit B(int) { b = 2; }
};

struct C : B {
    using B::B;
};

constexpr B b = 0;
static_assert( b.b == 1 ); //ok everywhere

constexpr C c = 0;
static_assert( c.b == 1 ); //ok in Clang only

Demo: https://gcc.godbolt.org/z/hva4f5qs5

As quoted in related GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85251 : [namespace.udecl] p13 does specify what should happen here:

Constructors that are named by a using-declaration are treated as though they were constructors of the derived class when looking up the constructors of the derived class ([class.qual]) or forming a set of overload candidates ([over.match.ctor], [over.match.copy], [over.match.list]).

So explicit constructor from B shall remain explicit in C after using B::B;, and only Clang correctly process the above program at this moment.

And even easier way to see that other compilers are wrong, is to remove B(auto) constructor:

struct B {
    int b;
    constexpr explicit B(int) { b = 2; }
};

struct C : B {
    using B::B;
};

constexpr C c = 0; // error everywhere

Now all compilers correctly reject the code (demo: https://gcc.godbolt.org/z/P3zqxMvvn) even though they did not call the removed constructor in the previous example.