Что делать, если класс C ++ содержит как константную, так и неконстантную конструктор копирования ссылок?

фрагмент 1:

#include<iostream>
using namespace std;

class C{
public:
C(){}
C(const C& c){
cout<<"const copy constructor called"<<endl;
}
};
int main(){
C c1;
C c2 = c1;
return 0;
}

выход: const копирует конструктор


фрагмент 2:

#include<iostream>
using namespace std;

class C{
public:
C(){}
C(const C& c){
cout<<"const copy constructor called"<<endl;
}
C(C& c){
cout<<"non-const copy constructor called.\t "<<endl;
}
};
int main(){
C c1;
C c2 = c1;
return 0;
}

выход: неконстантный конструктор копирования


фрагмент 3:

#include<iostream>
using namespace std;

class C{
public:
C(){}
C(const C& c){
cout<<"const copy constructor called"<<endl;
}
C(C c){
cout<<"non-const copy constructor called.\t "<<endl;
}
};
int main(){
C c1;
C c2 = c1;
return 0;
}

выход: ошибка: конструктор копирования должен передать свой первый аргумент по ссылке


Я так запутался в:

  1. для фрагмента 2, почему конструктор неконстантного копирования здесь допустим? почему был вызван неконстантный конструктор копирования, а не константный.
  2. для фрагмента 3 я знаю, что конструктор копирования должен использовать ссылку const, чтобы избежать бесконечной рекурсии. Но вот класс С попал C(const C& c), C(C c) не вызовет бесконечной рекурсии, почему она до сих пор не работает?

5

Решение

Фрагмент 1: Один стандартный конструктор копирования с const T&, Счастливый мир

Фрагмент 2:

То, что вы фактически сделали, это перегружен конструктор копирования — тот, который принимает ссылку T& а другой, который принимает постоянную ссылку const T&,

Обратите внимание: любой конструктор для класса T, имеющий один обязательный аргумент типа T & или же const T & (он может также иметь дополнительные аргументы по умолчанию) является конструктором копирования.

Итак, для компилятора все сводится к поиску Наиболее подходящий для разрешения перегрузки, и это делается как:

Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем
стандартная последовательность преобразования S2, если:

  • ….
  • S1 и S2 являются ссылочными привязками (8.5.3), и типы, на которые ссылаются ссылки, относятся к одному и тому же типу, за исключением cv-квалификаторов верхнего уровня,
    и тип, к которому относится ссылка, инициализированная S2, является более
    cv-qualified, чем тип, к которому ссылка инициализирована S1
    относится.

Итак, написание

C c1;
C c2 = c1;

вызовет конструктор неконстантного копирования, так как он лучше соответствует, но,

пишу,

const C c1;
C c2 = c1;

вызовет конструктор копирования const (вы можете проверить), так как теперь конструктор копирования с const является единственным жизнеспособным соответствием.

Фрагмент 3 просто не подходит для компилятора.

C(C c){
cout<<"non-const copy constructor called.\t "<<endl;
}

У вас не может быть метода с подписью C(C c), Компилятор думает, что вы пытаетесь написать конструктор копирования и пропустили запись & и, следовательно, сообщает об ошибке. Удалите его, и он отлично работает.

@ Если у тебя нет веских причин, никогда не использовать C(C& c) для вашего копирования конструктора. Не пропускай const потому что изменение объекта, из которого вы делаете копию, не имеет особого смысла.

8

Другие решения

для фрагмента 2, почему конструктор неконстантного копирования здесь допустим? почему был вызван неконстантный конструктор копирования, а не константный.

Рассмотрите ваш код для этого вопроса, но с изменением ниже с комментарием // (*):

int main(){
const C c1; // (*) <- See change here
C c2 = c1;
return 0;
}

Это вызывает const скопируйте версию ctor. Это на самом деле не имеет ничего общего с функцией, которая оказывается конструктором — если функция имеет две перегрузки, одна берет ссылку, а другая — const ссылка, тогда неконстантные объекты будут вызываться с первым, и const объекты со вторым.

для фрагмента 3 я знаю, что конструктор копирования должен использовать ссылку const, чтобы избежать бесконечной рекурсии. Но здесь класс C получил C (const C& c), C (C c) не вызовет бесконечной рекурсии, почему она все еще не работает?

Рассмотрите следующий код и обратите внимание, что никакого вызова не происходит (содержимое main в значительной степени стерта).

#include<iostream>
using namespace std;

class C{
public:
C(){}
C(const C& c){
cout<<"const copy constructor called"<<endl;
}
C(C c){
cout<<"non-const copy constructor called.\t "<<endl;
}
};
int main(){
// Note that nothing is creating C instances at all.
return 0;
}

Это приводит к точно такой же ошибке — компилятор просто отказывается компилировать класс с этим интерфейсом, независимо от того, пытается ли что-то вызвать его или нет.

Цитата из ответа на этот вопрос «это запрещено стандартом в §12.8 / 3:

Объявление конструктора для класса X является некорректным, если его первый параметр имеет тип (необязательно cv-квалифицированный) X и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию.

«

3