Краткое объяснение: функции, которые возвращаются по значению, создают временный объект, который обрабатывается как постоянный и, следовательно, не может быть передан функциям, принимающим ссылки, он может быть передан только функциям, принимающим константную ссылку. Если вы действительно выделяете объект в get_a (), вам действительно нужно возвращать указатель (надеюсь, вы не забудете его удалить) или, в худшем случае, ссылку. Если вы действительно хотите вернуть копию - создайте объект в стеке.
Длинное объяснение: Чтобы понять, почему ваш код не компилируется, если есть только «конструктор неконстантного копирования» 1 , вам необходимо ознакомиться с терминами lvalue и Rvalue . Изначально они означали, что rvalue s может только стоять справа от оператора = (назначение), в то время как lvalue s может стоять также слева. Пример:
T a, b;
const T ac;
a = b; // a can appear on the left of =
b = a; // so can b => a and b are lvalues in this context
ac = a; // however, ac is const so assignment doesn't make sense<sup>2</sup>, ac is a rvalue
Когда компилятор выполняет разрешение перегрузки (обнаруживая, какая перегрузка функции / метода лучше всего соответствует предоставленным аргументам), он позволяет lvalue s сопоставлять параметры, передаваемые значением 3 , ссылочные и константные ссылочные типы. Однако он будет соответствовать rvalue s только со значением 3 и константными опорными параметрами. И это потому, что в некотором смысле, поскольку rvalue s не может быть помещено в левую часть оператора =, они имеют семантику только для чтения, и когда нельзя изменять их. И когда вы принимаете параметр через неконстантную ссылку, подразумевается, что вы каким-то образом измените этот параметр.
Последний кусочек головоломки: временные объекты значение с. Функция, возвращаемая по значению, создает временный объект с очень ограниченным сроком службы. Из-за своей ограниченной продолжительности жизни он считается const, и поэтому является rvalue . И это rvalue не сопоставляет функции с параметрами по неконстантной ссылке. Примеры:
void f_cref(const A& a) { std::cout << "const ref" << std::endl; }
void f_ref(A& a) { std::cout << "non-const ref" << std::endl; }
A geta() { return A(); }
A a;
const A ac;
f_ref(a); // ok, a is a lvalue
f_ref(ac); // error, passing const to non-const - rvalue as lvalue - it's easy to spot here
f_cref(a); // ok, you can always pass non-const to const (lvalues to rvalues)
f_ref(geta()); // error, passing temporary and therefore const object as reference
f_cref(geta()); // ok, temporary as const reference
Теперь у вас есть вся информация, чтобы выяснить, почему ваш код не компилируется. Конструктор копирования похож на обычные функции.
Я немного упростил вещи, так что лучшее, более полное и правильное объяснение можно найти в этом превосходном сообщении в блоге команды разработчиков Visual C ++ Studio о ссылках на rvalue , которое также затрагивает новую функцию C ++ 0x "rvalue" ссылки "
1 - нет такого понятия, как неконстантный конструктор копирования. Конструктор копирования принимает константную ссылку, точка.
2 - вы, вероятно, можете поместить объект const слева от =, если у него есть оператор = объявленный const. Но это было бы ужасно, ужасно, бессмысленно делать.
3 - на самом деле вы не сможете передать const A по значению, если A не имеет конструктора копирования - тот, который принимает const A &, то есть.