фрагмент 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;
}
выход: ошибка: конструктор копирования должен передать свой первый аргумент по ссылке
Я так запутался в:
C(const C& c)
, C(C c)
не вызовет бесконечной рекурсии, почему она до сих пор не работает?Фрагмент 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
потому что изменение объекта, из которого вы делаете копию, не имеет особого смысла.
для фрагмента 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 и либо нет других параметров, либо все остальные параметры имеют аргументы по умолчанию.
«