Foo f = Foo (); // нет подходящей функции для вызова 'Foo :: Foo (Foo)' ... да? - PullRequest
11 голосов
/ 05 мая 2010
class Foo
{
public:
    explicit Foo() {}
    explicit Foo(Foo&) {}
};

Foo d = Foo();

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

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

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

Что дает? Как мне решить это? (Это на GCC кстати)

Ответы [ 10 ]

12 голосов
/ 05 мая 2010

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

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

Foo d( (Foo()) );

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

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

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

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

Foo d;

и

Foo d = Foo();

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

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

5 голосов
/ 05 мая 2010

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

4 голосов
/ 06 мая 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
};

3 голосов
/ 06 мая 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();

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

3 голосов
/ 05 мая 2010

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

Foo foo = Foo()

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

Edit:

Это только половина ответа. См. Статью Чарльза Бейли или UncleBens о том, почему необходим const.

2 голосов
/ 05 мая 2010
Foo d = Foo();

должно быть

Foo d;

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

2 голосов
/ 05 мая 2010

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

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

Foo d;

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

Foo d(arguments);
1 голос
/ 05 мая 2010

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

Foo(const Foo&) {}
0 голосов
/ 05 мая 2010
class Foo
{
public:
    explicit Foo() {}
    explicit Foo(const Foo&) {}
};

Foo d = Foo()
0 голосов
/ 05 мая 2010

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

Foo (Foo const &) {}

Как правило, вы хотите сделать оба.

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

...