Поскольку оба ваших пользовательских конструктора являются шаблонами, они не являются конструкторами копирования (или перемещения). Таким образом, компилятор неявно объявляет конструктор копирования и определяет его как значение по умолчанию.
Часть 1, таким образом, сводится к следующей отличительной программе:
struct A {
struct B {} b;
constexpr A() {};
// constexpr A(A const& a) : b{a.b} {} // #1
};
int main() {
auto a = A{};
constexpr int i = (A{a}, 0);
}
Отклонено от Clang и MSV C, принятые gcc; раскомментируйте #1
для всех трех, чтобы принять.
Согласно определению неявно определенного конструктора копирования , нет никакого способа, которым #1
отличается от constexpr A(A const&) = default;
, поэтому g cc правильно. Также обратите внимание, что если мы дадим B
определяемый пользователем конструктор копии constexpr, Clang и MSV C снова примут, поэтому проблема заключается в том, что эти компиляторы не могут отслеживать конструктивность копии constexpr рекурсивно пустых неявно копируемых классов. Поданные ошибки для MSV C и Clang ( исправлено для Clang 11).
Часть 2:
Удаление #1
означает, что вы копируете (выполняете преобразование lvalue в rvalue) объект s.b
типа int
, время жизни которого начинается вне контекста constexpr.
Удаление #2
дает S
определяемый пользователем constexpr
конструктор копирования, который затем делегируется в #4
.
Удаление #3
дает S
определяемый пользователем (неконстантный) конструктор копирования, подавляя неявно- определенный конструктор копирования, поэтому делегирующая конструкция вызывает конструктор const шаблона (который, помните, не является конструктором копирования).
Удаление #4
означает, что ваш шаблон конструктора с аргументом S& other
больше не вызывает неявно -определенный конструктор копирования, поэтому b
инициализируется по умолчанию, что Clang может делать в контексте constexpr. Обратите внимание, что конструктор копирования все еще неявно объявляется и определяется как дефолтный, просто ваш конструктор template<class...> S::S(S& other)
предпочитается разрешением перегрузки.
Важно признать различие между , подавляющим неявно определенный конструктор копирования и предоставление предпочтительной перегрузки. template<class...> S::S(S&)
не подавляет неявно определенный конструктор копирования, но предпочтительнее для неконстантного аргумента lvalue, при условии, что неявно определенный конструктор копирования имеет аргумент S const&
. С другой стороны, template<class...> S::S(S const&)
не подавляет неявно определенный конструктор копирования и никогда не может быть предпочтительнее неявно определенного конструктора копирования, поскольку это шаблон и списки параметров одинаковы.