#c #implicit-conversion #overload-resolution #explicit #constructor-overloading
#c #неявное преобразование #перегрузка-разрешение #явный #конструктор-перегрузка
Вопрос:
#include <iostream>
#include <string>
struct mystruct{
mystruct(std::string s){
std::cout<<__FUNCTION__ <<" String "<<s;
}
explicit mystruct(bool s) {
std::cout<<__FUNCTION__<<" Bool "<<s;
}
};
int main()
{
const char* c ="hello";
mystruct obj(c);
return 0;
}
вывод:
mystruct Bool 1
- Почему
const char*
неявно преобразуется вbool
, а неstd::string
, хотя конструктор требуетexplicit
тип? - Как здесь применяется неявный приоритет преобразования?
Комментарии:
1. Потому что это указатель, и все указатели, естественно, могут быть неявно преобразованы в
bool
значения.2. @Someprogrammerdude: но конструктор помечен как
explicit
, почему он не ссылается на тип?3. Прямая инициализация , которую вы выполняете, всегда является явной. Другое дело, если бы вы копировали инициализацию (например
mystruct obj = c;
) или использовалиc
в другом контексте, гдеmystruct
ожидался объект (например, вызов функции, ожидающейmystruct
объект по значению).4. В этом случае вы можете использовать
std::enable_if
для выбора нужного конструктора.5.
explicit
отключает неявное преобразованиеX
вmystruct
, а не неявное преобразование типов аргументов в конструктор.
Ответ №1:
Потому что неявное преобразование из const char*
to bool
квалифицируется как стандартное преобразование, в то время const char*
как to std::string
является определяемым пользователем преобразованием. Первый имеет более высокий рейтинг и выигрывает в разрешении перегрузки.
Стандартная последовательность преобразования всегда лучше, чем определяемая пользователем последовательность преобразования или последовательность преобразования с многоточием.
Кстати: mystruct obj(c);
выполняется прямая инициализация, explicit
включая конструкторы преобразования mystruct::mystruct(bool)
. В результате c
преобразуется в bool
, а затем передается в mystruct::mystruct(bool)
качестве аргумента для построения obj
.
Прямая инициализация более разрешительна, чем инициализация копирования: инициализация копирования учитывает только неявные конструкторы и неявные пользовательские функции преобразования, в то время как прямая инициализация учитывает все конструкторы и все пользовательские функции преобразования.
- Указывает, что конструктор
or conversion function (since C 11)
or deduction guide (since C 17)
является явным, то есть его нельзя использовать для неявных преобразований и инициализации копирования.
Комментарии:
1. По моему скромному мнению, это СТРАННО. Тогда в чем смысл
explicit
?2. Это останавливает случайные преобразования, например: godbolt.org/z/heErYe или godbolt.org/z/7hWdon , если бы вы не могли вызвать явный конструктор так, как вы его написали, вы бы вообще никогда не смогли вызвать этот конструктор
3. @manuell Это вступает в силу при инициализации копирования , но не имеет значения при прямой инициализации.
4. @AlanBirtles Не уверен, что вы имеете в виду во второй части вашего комментария, как насчет
mystruct obj( static_cast<bool>(c));
? Я думаю, что это то, что OP ожидает, что потребуется для вызова явного ctr5. @manuell может быть, это помогает думать об этом так: в строке
mystruct obj(c);
явно указано, что вы явно создаете amystruct
, в то время как вы не можете вызвать afoo(mystruct)
с помощью abool
напрямую. Это созданиеmystruct
явного, а не типа параметра для конструктора
Ответ №2:
«Почему const char*
неявно преобразуется в bool
, а не std::string
, хотя конструктор требует explicit
тип?»:
- Стандартное преобразование предпочтительнее пользовательских преобразований.
char const*
является указателем на постоянный символ, и указатель может быть неявно преобразован в bool
: в случае, если это nullptr
так, он преобразуется в false
противном случае в true
.
- Раньше вы видели такое эффективное преобразование в условиях, когда вы проверяете, является ли указатель
NULL
или нет, поэтому, если это неnulptr
так, мы безопасно удаляем ссылку на него, иначе он имеетnullptr
значение, поэтому удалять ссылку на него некорректно:int* ptr = nullptr; if(ptr) // false because ptr has nullptr or NULL or 0 or 0x000000 address value std::cout << ptr << 't' << *ptr << 'n'; // not executed ptr = new int(10); // valid and non-nullptr if(ptr) // non-nullptr so condition succeeds std::cout << ptr << 't' << *ptr << 'n'; // 0FED155 10 delete ptr; // free memory
explicit
конструктор означает, что его можно вызвать только явно, и единственный способ — это прямая инициализация, как в вашем случае:mystruct obj(c); // direct initialization mystruct obj = c; // copy-initialization. Error: constructor myStruct(bool) is `explicit`