Foo f = Foo (); // нет соответствующей функции для вызова 'Foo :: Foo (Foo)', а?

class Foo
{
public:
    explicit Foo() {}
    explicit Foo(Foo&) {}
};

Foo d = Foo();

ошибка: нет соответствующей функции для вызова 'Foo :: Foo (Foo)'

Я попытался изменить Foo(Foo&) на Foo(Foo), поскольку ошибка подсказывает, что AFAIK не является допустимым конструктором, и, конечно же, я получаю:

ошибка: недопустимый конструктор; вы, вероятно, имели в виду «Foo (const Foo &)»

Что дает? Как мне решить эту проблему? (Кстати, это есть на GCC)


person Kyle    schedule 05.05.2010    source источник
comment
Компилятор уже ответил на ваш вопрос ... Foo (const Foo&). Foo d = Foo(); вызывает конструктор копирования.   -  person Brian Roach    schedule 05.05.2010
comment
+1, потому что, кажется, никто не знает ответа   -  person BlueRaja - Danny Pflughoeft    schedule 05.05.2010
comment
+1 только за URL ... фуфууууу: D   -  person jalf    schedule 06.05.2010
comment
Брайан Роуч прав, компилятор ответил на ваш вопрос! Вы не можете сделать так, чтобы конструктор копирования взял копию Foo, потому что он должен был бы взять копию для входа в конструктор копирования, который будет рекурсивно повторяться бесконечно ... поэтому он предлагает правильную подпись.   -  person AshleysBrain    schedule 06.05.2010


Ответы (10)


В конструкторе копирования есть две сомнительные вещи.

Во-первых, вы сделали конструктор копирования явным (что сомнительно), поэтому вам (теоретически) нужно сделать:

Foo d( (Foo()) );

Во-вторых, ваш конструктор копирования принимает ссылку, а не ссылку const, что означает, что вы не можете использовать его с временным Foo.

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

Обратите внимание, что explicit в вашем конструкторе по умолчанию не имеет никакого эффекта. [*] explicit влияет только на конструкторы, которые могут быть вызваны с одним параметром. Это предотвращает их использование для неявных преобразований. Для конструкторов, которые принимают только ноль или только два или более параметров, это не действует.

[Примечание: может быть разница между:

Foo d;

и

Foo d = Foo();

но в этом случае у вас есть объявленный пользователем конструктор по умолчанию, поэтому это не применимо.]

Изменить: [*] Я только что дважды проверил это, и в 12.3.1 [class.conv.ctor] сказано, что вы можете создать конструктор по умолчанию explicit. В этом случае конструктор будет использоваться для выполнения инициализации по умолчанию или инициализации значения. Честно говоря, я не понимаю значения этого, как если бы у вас есть объявленный пользователем конструктор, тогда это не-POD-тип, и даже локальные объекты не-POD-типа инициализируются по умолчанию, если у них нет инициализатора в этом разделе говорится, что это может быть сделано explicit конструктором по умолчанию. Возможно, кто-то может указать на крайний случай, когда это действительно имеет значение, но пока я не вижу, какое влияние explicit оказывает на конструктор по умолчанию.

person CB Bailey    schedule 05.05.2010
comment
Почему лишние скобки? - person clahey; 05.05.2010
comment
@clahey: Чтобы обойти самый неприятный синтаксический анализ. - person James McNellis; 05.05.2010
comment
Теперь, когда Джеймс дал правильный ответ, я могу просто сказать: потому что я чокнутый фанат шепелявости. - person CB Bailey; 05.05.2010
comment
Я сделаю то же самое, что и со всеми вопросами, на которые не знаю ответа: проголосуйте за тот, который кажется наиболее правильным. Поздравляю, Чарли. - person BlueRaja - Danny Pflughoeft; 06.05.2010
comment
Следующий вопрос. Разница между Foo d; и Foo d = Foo (); заключается в том, что в одном случае вы вызываете конструктор по умолчанию, а в другом вы создаете временный, вызываете конструктор по умолчанию, а затем вызываете конструктор копирования. Если предположить, что два конструктора имеют правильную семантику, насколько полезен второй случай? - person clahey; 06.05.2010
comment
@clahey: Если у вас есть тип класса без конструктора, объявленного пользователем, или тип POD, то Foo d; оставляет d неинициализированным, а значение Foo d = Foo(); инициализирует d. Что касается вызова конструктора копирования, компилятору разрешено игнорировать вызов конструктора копирования (т. Е. Фактически не вызывать его), пока конструктор копирования является вызываемым. По сути, это позволяет создавать объект на месте, а не создавать временное и затем копировать его. - person James McNellis; 06.05.2010
comment
@clahey: Внутри шаблона и параметра шаблона Foo конструкция Foo d = Foo() полезна, потому что, если Foo является примитивным типом, то d остается неинициализированным, если использовалось Foo d. Связано: http://stackoverflow.com/questions/2143022/how-to-correctly-initialize-variable-of-template-type - person Christian Ammer; 06.05.2010
comment
Я хочу принять этот вопрос, но пока не могу. У меня проблема с этим утверждением: обратите внимание, что явное значение для вашего конструктора по умолчанию не имеет никакого эффекта. В Visual Studio explicit Foo() {} приводит к ошибке C2520: 'Foo :: Foo': неявный конструктор недоступен для неявного преобразования, тогда как только Foo() {} приводит к ошибке C2558: класс 'Foo': конструктор копирования недоступен или конструктор копирования объявлен 'явным ', поэтому явное значение конструктора по умолчанию имеет эффект. Но я не понимаю, ПОЧЕМУ. - person Kyle; 06.05.2010
comment
@Kyle: Я обновил свой ответ. Стандарт позволяет сделать конструктор по умолчанию явным, но вы все равно должны иметь возможность инициализировать значение с ним в Foo d = Foo();, поэтому я могу только предположить, что это ошибка Visual Studio, хотя и довольно неясная. - person CB Bailey; 06.05.2010

Вы не хотите отмечать любой из этих конструкторов как явный - компилятор должен использовать их оба, особенно конструктор копирования, неявно. Чего вы пытаетесь достичь, отмечая их явным образом?

person Community    schedule 05.05.2010
comment
Чтобы ответить на ваш вопрос: я хочу устранить любые сюрпризы, вызванные преобразованием, которого я не хотел (... из-за того, что компилятор выдает ошибку). - person Kyle; 06.05.2010
comment
@Kyle Но вы же хотите, чтобы компилятор мог делать копии - это не преобразование. И это тоже не конструкция по умолчанию. - person ; 06.05.2010
comment
Верно, но я хочу, чтобы он взорвался, если я когда-нибудь попытаюсь сделать Foo f = Bar(), где Bar с радостью позволяет преобразовать себя в Foo, предоставив operator Foo() (я думаю, что этот мыслительный процесс у меня правильный ...) - person Kyle; 06.05.2010
comment
@ Кайл, я думаю, что нет. Конструктор копирования не выполняет преобразования - если вы хотите предотвратить этот код, вам нужно предотвратить преобразование Bar в Foo, а не запретить копирование Foo. Также существует проблема почему вы хотите предотвратить преобразования в первую очередь - они, как правило, полезны и придают C ++ большую мощь и удобство использования. - person ; 06.05.2010

Во-первых, ни конструктор по умолчанию, ни конструктор копирования никогда не должны быть explicit. Вам нужно создать конструктор explicit только в том случае, если он принимает единственный аргумент какого-либо другого типа, чтобы предотвратить неявное преобразование из этого типа. Конструктор копирования принимает ссылку на сам класс, поэтому опасность нежелательного преобразования отсутствует.

Во-вторых, убедитесь, что конструктор копирования принимает ссылку const.

В-третьих, Foo f; - это правильный способ создать объект класса foo по умолчанию. Обратите внимание, что Foo f(); неверно, потому что компилятор интерпретирует это как объявление функции f(), которая возвращает объект класса Foo.

В-четвертых, если вы написали свой собственный конструктор копирования, вам также следует написать оператор присваивания.


class Foo
{
  Foo() {} // no need to make explicit.  Nothing to convert from.

  Foo(const &Foo f) {} // again, nothing wrong with conversion from Foo to Foo

  explicit Foo(int a) {}  // need explicit to prevent accidental passing of an int
                          // to a function that takes Foo as an argument
};

person Dima    schedule 05.05.2010

Попробовать без явного? Я так думаю:

Foo foo = Foo()

создает неявную копию, поэтому конструктор явной копии не запускается.

Редактировать:

Это только половина ответа. См. Пост Чарльза Бейли или UncleBens, чтобы узнать, почему нужна const.

person clahey    schedule 05.05.2010
comment
Это правильный ответ. Более того, нет причин делать конструктор копирования явным для начала. Явные конструкторы предназначены для предотвращения случайного неявного преобразования типов. - person Edward Strange; 06.05.2010

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

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

Foo f = Foo();
        ^^^^^
          |
          --- this is a temporary that cannot be passed to a function
              that accepts a non-const reference

Кроме того, нет причин делать конструктор по умолчанию явным: это ключевое слово имеет смысл только для конструкторов (кроме конструктора копирования), которые могут быть вызваны ровно с одним аргументом, и в этом случае оно предотвращает неявные преобразования. других типов в Foo через этот конструктор. Например, если конструктор, принимающий int, был явным, такие ситуации не компилировались:

Foo f;
f = 1;  //assuming no operator= overload for (types convertible from) int
        //this implicitly performs f = Foo(1);

Foo g = 10;

void x(Foo);
x(20);

В конце концов:

class Foo
{
public:
    Foo();
    Foo(const Foo&);
    //...
};

Foo x = Foo();

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

person UncleBens    schedule 05.05.2010
comment
На данный момент это лучший ответ, но я бы добавил больше текста о том, что делать правильно. - person clahey; 06.05.2010
comment
Вы можете вызвать явный конструктор копирования, используя прямую инициализацию (например, Foo f; Foo g(f);). - person James McNellis; 06.05.2010
comment
@James: Спасибо, добавил пояснение. - person UncleBens; 06.05.2010
comment
+1 За 2-е предложение и следующий пример. Точно, почему у OP проблема. - person Nathan Ernst; 06.05.2010

Foo d = Foo();

должно быть

Foo d;

Первая строка создает экземпляр Foo, а затем копирует его в d;

person Tom    schedule 05.05.2010
comment
Правильно, но не ответ на вопрос - Foo d = Foo() был просто упрощением для генерации ошибки. Я мог бы иметь Foo d; и Foo k = d; где-нибудь еще. - person Kyle; 06.05.2010

Ваша проблема в создании экземпляра. Вам не нужен Foo d = Foo(); для конструктора по умолчанию.

Сохраните свой класс таким же, но попробуйте это для создания экземпляра:

Foo d;

Фактически, вам даже не нужно Foo d = Foo(arguments); для построения с параметрами. Это должно быть так:

Foo d(arguments);
person Randolpho    schedule 05.05.2010
comment
Foo d = Foo() был просто упрощением для генерации ошибки. Я мог бы иметь Foo d; и Foo k = d; где-нибудь еще - person Kyle; 06.05.2010
comment
@Kyle: Судя по многочисленным комментариям, которые вы оставили, я вижу, что вы намеренно пытались вызвать конструктор копирования. Я бы хотел, чтобы вы прямо сказали об этом в своем вопросе. Я не могу найти нигде в вашем вопросе, чтобы вы имели в виду, что вы вообще знаете, что такое конструктор копирования ; вы только что разместили код и сказали, почему он ломается? Я сделал предположение (да, да), что вы просто делаете это неправильно. Foo d = Foo();, безусловно, вызывает конструктор копирования на этом мимолетном Foo экземпляре, но на самом деле вам никогда не следует делать. - person Randolpho; 06.05.2010
comment
Как подразумевается в заголовке, мой вопрос касается странного сообщения об ошибке, которое не кажется очевидным результатом данного кода. Мне еще предстоит овладеть навыком постановки вопроса в угол настолько точно, чтобы никто не смог ответить на неправильный вопрос. Пожалуйста, отредактируйте свой ответ, чтобы я мог проголосовать за вас. - person Kyle; 06.05.2010
comment
@Kyle: Хех ... Ошибки компилятора C ++ в целом имеют тенденцию к странному. Что касается вашего вопроса, я бы сказал, что прямое указание вашего намерения - это хорошо (tm). В этом случае ... предположим, вы намеревались вызвать конструктор копирования, но компилятор сказал, что у вас его нет, хотя вы явно это сделали, так что WTF? Что касается сделанного мной предположения, не вините себя, вините меня. Проблема в том, что так много людей публикуют мусорные вопросы, что нам приходится пытаться найти настоящий вопрос. Я должен был быть более прилежным. - person Randolpho; 06.05.2010

Компилятор говорит вам ... Используйте это:

Foo(const Foo&) {}
person KZ.    schedule 05.05.2010
comment
Противоположное голосование. Это буквально говорит о том, как должна выглядеть подпись. (Не то, что он должен делать - хотя ничего.) - person UncleBens; 06.05.2010

Вы можете решить проблему двумя способами. Один из них (уже предложенный Randolpho) - отказаться от использования copy ctor. Другой - написать правильную копию ctor:

Foo (Foo const &) {}

Обычно вы хотите делать и то, и другое.

Изменить: глядя на это, мой последний комментарий легко неверно истолковать. Довольно многим классам не вообще нужен ctor копирования, но если вам нужен ctor копирования, он обычно должен иметь форму, указанную выше (не явную, и ссылка на const в качестве параметра).

person Jerry Coffin    schedule 05.05.2010
comment
Foo(Foo&) - правильный конструктор копирования. Это просто не тот конструктор копирования для того, что он пытается сделать. - person Dennis Zickefoose; 06.05.2010
comment
@ Деннис: Если вам это нравится, это нормально, но конструктор копирования, который открывает дверь для изменения копируемого объекта, - это не то, что я бы назвал правильным. - person Jerry Coffin; 06.05.2010

person    schedule
comment
это был не я, но могу сказать, что все еще не компилируется. - person Kyle; 06.05.2010