Почему const char* неявно преобразуется в bool, а не в std::string ?

#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
 
  1. Почему const char* неявно преобразуется в bool , а не std::string , хотя конструктор требует explicit тип?
  2. Как здесь применяется неявный приоритет преобразования?

Комментарии:

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 .

Прямая инициализация более разрешительна, чем инициализация копирования: инициализация копирования учитывает только неявные конструкторы и неявные пользовательские функции преобразования, в то время как прямая инициализация учитывает все конструкторы и все пользовательские функции преобразования.

О explicit спецификаторе,

  1. Указывает, что конструктор 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 ожидает, что потребуется для вызова явного ctr

5. @manuell может быть, это помогает думать об этом так: в строке mystruct obj(c); явно указано, что вы явно создаете a mystruct , в то время как вы не можете вызвать a foo(mystruct) с помощью a bool напрямую. Это создание 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`