4

Update:
Thank you, Jamboree.
This is the final struct A.

struct A
{
    template<class ... Args,class=std::enable_if_t<(sizeof...(Args)!=1)>>
    A(Args &&...args)
    {
        cout<<'v';
    }
    template<class Arg,class=std::enable_if_t<!std::is_base_of<std::remove_reference_t<Arg>,A>::value>>
    A(Arg &&arg)
    {
        cout<<'v';
    }
    A(const A &)
    {
        cout<<'c';
    }
    A(A &&)
    {
        cout<<'m';
    }
};

Origin:
About this code,

#include<iostream>
#include<type_traits>
#include<utility>
using namespace std;

struct A
{
    template<class ... Args,class=std::enable_if_t<
        sizeof...(Args)!=1
        ||!std::is_same<std::remove_cv_t<std::remove_reference_t<Args>>,A>::value>>
    A(Args &&...args)
    {
        cout<<'v';
    }
    A(const A &)
    {
        cout<<'c';
    }
    A(A &&)
    {
        cout<<'m';
    }
};

int main()
{
    A a{10};
    A b{10,20};
    A c{b};
    const A d{c};
    A e{move(c)};
}

The output is vvvvm in VC++ 14.0.
But why is the output not vvccm?
(I want c and d to use copy constructor. And I know Effective Modern C++ Item 27 use only one forwarding reference.)

Caesar
  • 971
  • 6
  • 13

1 Answers1

3

Because b and c, is A& to the compiler (not const-qualified) when you pass them to the ctor of c and d, Args &&...args is thus a better match than const A &.

To achieve what you want, you could do it this way:

struct A
{
    A() = default;

    template<class ... Args,std::enable_if_t<(sizeof...(Args)>1), bool> = true>
    A(Args &&...args)
    {
        cout<<'v';
    }

    template<class Arg,std::enable_if_t<!std::is_base_of<A, std::remove_reference_t<Arg>>::value, bool> = true>
    A(Arg && arg)
    {
        cout<<'v';
    }

    A(const A &)
    {
        cout<<'c';
    }
    A(A &&)
    {
        cout<<'m';
    }
};
Jamboree
  • 5,139
  • 2
  • 16
  • 36
  • In this example, a default constructor is always needed. If not specified explicitly, make sure to write `sizeof...(Args)!=1` instead – JVApen Jan 12 '19 at 13:10