29

Are variadic constructors supposed to hide the implicitly generated ones, i.e. the default constructor and the copy constructor?

struct Foo
{
    template<typename... Args> Foo(Args&&... x)
    {
        std::cout << "inside the variadic constructor\n";
    }
};

int main()
{
    Foo a;
    Foo b(a);
}

Somehow I was expecting this to print nothing after reading this answer, but it prints inside the variadic constructor twice on g++ 4.5.0 :( Is this behavior correct?


It also happens without variadic templates:

struct Foo
{
    Foo()
    {
        std::cout << "inside the nullary constructor\n";
    }

    template<typename A> Foo(A&& x)
    {
        std::cout << "inside the unary constructor\n";
    }
};

int main()
{
    Foo a;
    Foo b(a);
}

Again, both lines are printed.

Community
  • 1
  • 1
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 1
    I just ran a quick test on gcc45, and a regular, non-variadic template constructor prevents creation of a compiler generated default constructor as well. My suspicion is that the rules changed in C++0x. – Dennis Zickefoose Jun 01 '10 at 22:28
  • 2
    @Dennis: I'd be _really_ surprised if C++0x would change rules that will be introduced with C++0x. `:)` – sbi Jun 01 '10 at 22:49
  • @Dennis So is the linked answer bogus? It says "a template constructor or assignment operator will not suppress the compiler generated one". – fredoverflow Jun 01 '10 at 22:53
  • @sbi: What I was saying is that, even without C++0x features, gcc45 doesn't act the way he thinks its supposed to. So either the rules changed in C++0x, gcc is nonstandard in this case, or that other guy was wrong. Assuming the rules changed makes two people right, so its the democratic stance to take ;) – Dennis Zickefoose Jun 01 '10 at 22:58
  • Honestly, I don't know. Templates confuse me as a rule. – Dennis Zickefoose Jun 01 '10 at 23:01

1 Answers1

22

Declaration of the implicitly declared copy constructor is not, in fact, being suppressed. It's just not being called due to the rules of overload resolution.

The implicitly declared copy constructor has the form Foo(const Foo&). The important part of this is that it takes a const reference. Your constructor template takes a non-const reference.

a is not const, so the non-const user-declared constructor template is preferred over the implicitly-declared copy constructor. To call the implicitly-declared copy constructor, you can make a const:

const Foo a;
Foo b(a);

or you can use static_cast to obtain a const reference to a:

Foo a;
Foo b(static_cast<const Foo&>(a));

The overload resolution rules that describe this are found mostly in §13.3.3.2/3 of the C++0x FCD. This particular scenario, with a combination of lvalue and rvalue references, is sort of described by the various examples on page 303.


A variadic constructor template will suppress the implicitly declared default constructor because a variadic constructor template is user-declared and the implicitly declared default constructor is only provided if there are no user-declared constructors (C++0x FCD §12.1/5):

If there is no user-declared constructor for class X, a constructor having no parameters is implicitly declared as defaulted.

A variadic constructor template will not suppress the implicitly declared copy constructor because only a non-template constructor can be a copy constructor (C++0x FCD §12.8/2, 3, and 8):

A non-template constructor for class X is a copy constructor if its first parameter is of type X&, const X&, volatile X& or const volatile X&, and either there are no other parameters or else all other parameters have default arguments.

A non-template constructor for class X is a move constructor if its first parameter is of type X&&, const X&&, volatile X&&, or const volatile X&&, and either there are no other parameters or else all other parameters have default arguments.

If the class definition does not explicitly declare a copy constructor and there is no user-declared move constructor, a copy constructor is implicitly declared as defaulted.

Community
  • 1
  • 1
James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • @James "a template constructor [...] is never a copy constructor" -> so g++ 4.5.0 is buggy and the second line should not print anything? And by FCD do you mean n3090? – fredoverflow Jun 01 '10 at 22:55
  • @Fred: The FCD is n3092; I had to update the quotes because I had n3035 open for some reason and there are some changes in n3092... sorry about that. – James McNellis Jun 01 '10 at 23:03
  • @James It happens with non-variadic templates as well, I have updated my question! – fredoverflow Jun 01 '10 at 23:55
  • @Fred: See the update; the behavior you are seeing is due to the overload resolution rules (so, it's not a bug in g++). – James McNellis Jun 02 '10 at 00:19
  • @FredOverflow, GCC's behavior is compliant to rules of C++03 (on a reasonable interpretation - but the Standard was not actually clear). C++0x has the rules stated clear and GCC will have to change its behavior (like @James says, only n3092 made the rules clear - all the previous drafts weren't clear either). – Johannes Schaub - litb Jun 02 '10 at 21:42