Rule A14-5-1 (required, implementation, automated)

A template constructor shall not participate in overload resolution for a single argument of the enclosing class type.

Rationale

A template constructor is never a copy or move constructor and therefore doesn’t prevent the implicit definition of a copy or move constructor even if the template constructor looks similar and might easily be confused. At the same time, copy or move operations do not necessarily only use a copy or move constructor, but go through the normal overload resolution process to find the best matching function to use. This can cause confusion in the following cases: a template constructor that looks like a copy/move constructor is not selected for a copy/move operation because the compiler has generated an implicit copy/move constructor as well a template constructor is selected in preference over a copy/move constructor because the template constructor is a better match

To avoid these confusing situations, template constructors shall not participate in overload resolution for a single argument of the enclosing class type to avoid a template constructor being selected for a copy/move operation. It also makes it clear that the constructor is not a copy/move constructor and that it does not prevent the implicit generation of copy/move constructors.

Example

// $Id: A14-5-1.cpp 309903 2018-03-02 12:54:18Z christof.meerwald $
#include <cstdint>
#include <type_traits>

class A
{
public:
// Compliant: template constructor does not participate in overload
//
resolution for copy/move operations
template<typename T,

std::enable_if_t<! std::is_same<std::remove_cv_t<T>, A>::value> * = nullptr>

A(const T &value)
: m_value { value }
{}

private:
std::int32_t m_value;
};

void Foo(A const &a)
{
A myA { a }; // will use the implicit copy ctor, not the template converting
ctor

A a2 { 2 }; // will use the template converting ctor
}

class B
{
public:
B(const B &) = default;
B(B &&) = default;

// Compliant: forwarding constructor does not participate in overload
//
resolution for copy/move operations
template<typename T,
std::enable_if_t<! std::is_same<std::remove_cv_t<std::
remove_reference_t<T>>, B>::value> * = nullptr>

B(T &&value);

};

void Bar(B b)
{

B myB { b }; // will use the copy ctor, not the forwarding ctor
}

class C
{
public:
C(const C &) = default;
C(C &&) = default;

// Non-Compliant: unconstrained template constructor
template<typename T>
C(T &);
};

void Bar(C c)
{
C myC { c }; // will use template ctor instead of copy ctor
}

See also

MISRA C++ 2008 [7]: M14-5-2: A copy constructor shall be declared when there is a template constructor with a single parameter that is a generic parameter.