C ++: ошибка компиляции с явным ключевым словом - PullRequest
2 голосов
/ 28 марта 2020

Следующий код выдает ошибку компиляции:

#include <stdio.h>

class Option
{
    Option() { printf("Option()\n"); };
public:
    explicit Option(const Option& other)
    {
        printf("Option(const)\n");
        *this = other;
    }

    explicit Option(Option& other)
    {
        printf("Option(non-const)\n");
        *this = other;
    }

    explicit Option(const int&)
    {
        printf("Option(value)\n");
    }
};

void foo(Option someval) {};

int main()
{
    int val = 1;
    Option x(val);
    foo(x);
}

Выдается ошибка:

main.cpp:31:10: error: no matching function for call to ‘Option::Option(Option&)’
     foo(x);
          ^
main.cpp:5:5: note: candidate: ‘Option::Option()’
     Option() { printf("Option()\n"); };
     ^~~~~~
main.cpp:5:5: note:   candidate expects 0 arguments, 1 provided
main.cpp:25:6: note:   initializing argument 1 of ‘void foo(Option)’
 void foo(Option someval) 

Ошибка исчезнет, ​​если я удалю явное ключевое слово из explicit Option(const Option& other)

Может кто-нибудь объяснить мне, что является причиной ошибки компиляции? Кроме того, если есть разница между explicit Option(const Option& other) и explicit Option(Option& other)?

1 Ответ

2 голосов
/ 28 марта 2020

В вызове foo(x) необходимо создать новый Option, который станет someVal во время выполнения тела foo. То есть x необходимо скопировать в someVal. Компилятор пытается инициализировать Option someVal(x); (сначала Option(Option&), затем Option(Option const&)), но не может, потому что вы сказали, что оба этих конструктора explicit и не должны вызываться неявно. В C ++ 17 вы можете явно вставить отсутствующий вызов конструктора, чтобы он работал: foo(Option(x)). До C ++ 17 невозможно вызвать foo, потому что компилятор будет пытаться вставить вызовы конструктора в Option, но ни один не доступен для вставки.

На языке стандарта вызов функции, такой как foo(x), требует, чтобы параметр someVal был инициализированным при копировании из x. При инициализации копирования объекта определенного класса из объекта этого класса или производного класса учитываются только преобразовательные конструкторы этого целевого класса. «Конвертирующий конструктор» - это просто причудливое имя для «конструктора, который не explicit». Лучший конструктор из них затем выбирается с помощью нормального разрешения перегрузки. Поскольку ни один ваших конструкторов не является explicit, это всегда дает сбой и foo не вызывается до C ++ 17. Начиная с C ++ 17, когда аргумент является prvalue (как в foo(Option(x))), требование об вызове конструктора может быть отменено, и foo становится вызываемым.

На ваш вопрос:

Кроме того, если есть разница между explicit Option(const Option& other) и explicit Option(Option& other)?

Конечно: первый обещает, что не изменит свой аргумент, а второй нет , Вы уже знаете, что они могут быть определены для разных целей, и разрешение перегрузки предпочтет одно над другим, в зависимости от контекста:

Option x(1);
Option const y(2);
Option a(x); // calls Option(Option&) if available, which may modify x; calls Option(Option const&) if not, which shouldn't modify x
Option b(y); // must call Option(Option const&) because that promises not to modify y; cannot call Option(Option&) because it may modify y
...