2016-07-22 10 views
16

Pytanie: Czy niejawne konwersje bool zawsze polegają na próbie niejawnej konwersji na void*? (Jeśli taka funkcja konwersji istnieje dla typu). Jeśli tak, dlaczego?C++, czy konwersja bool zawsze wraca do niejawnej konwersji na void *?

Rozważmy następujący krótki program:

#include <iostream> 

class Foo{ 
public: 

    operator void*() const 
    { 
     std::cout << "operator void*() const" << std::endl; 
     return 0; 
    } 
}; 

int main() 
{ 
    Foo f; 

    if(f) 
     std::cout << "True" << std::endl; 
    else 
     std::cout << "False" << std::endl; 

    return 0; 
} 

Wynikiem tego programu jest:

operator void*() const 
False 

znaczenie, funkcję konwersji do void* nazwano. Jeśli otagujemy kwalifikator explicit przed funkcją konwersji, wówczas niejawna konwersja na void* może się nie powieść.

Edit: Wydaje się, że wiele odpowiedzi "null wskaźniki mogą być konwertowane do false". Rozumiem to, moje pytanie dotyczyło "jeśli nie mogę bezpośrednio wywołać operator bool(), wtedy spróbuję konwersji do dowolnego wskaźnika".

+0

"postara konwersję do dowolnego wskaźnika" - 'void' wskaźnik nie jest" dowolny "wskaźnik. Wskaźnik 'void' jest wskaźnikiem' void', nic więcej. Akceptuje tylko adresy wszystkich typów. – xinaiz

+1

@ BlackMoney Miałem na myśli dowolny typ wskaźnika, który rzeczywiście wydaje się być w przypadku, biorąc pod uwagę akceptowaną odpowiedź. – jensa

+0

@JesperJuhl To nie jest głupie pytanie. Pyta o to, czy kompilator może to zrobić, i o uzasadnienie. Może to być dla ciebie oczywiste, ale dla początkujących i średnio zaawansowanych deweloperów może to być mylące. –

Odpowiedz

15

Jeśli kompilator nie może bezpośrednio przekonwertować typu zdefiniowanego przez użytkownika na bool, to próbuje to zrobić pośrednio, tj. Przekonwertować (przez konwersję zdefiniowaną przez użytkownika) na typ, który można przekonwertować na bool bez angażowania innego użytkownika- zdefiniowana konwersja. Wykaz takich grup obejmują (i wydaje się być ograniczone do) następujące typy:

  • typ całkowitą arytmetyczna (char, int itp)
  • zmiennoprzecinkowych typu arytmetyczna (float, double, long double)
  • typ wskaźnika (void* należy tutaj, ale może to być równie dobrze const std::vector<Something>*)
  • wskaźnik do funkcji (w tym wskaźnik do funkcji składowej)
  • rodzaj odniesienia do żadnej z powyższych

jednak pamiętać, że tylko jedna taka pośrednia konwersja musi istnieć. Jeśli są możliwe co najmniej dwie konwersje z powyższej listy, kompilator napotka niejednoznaczność i zgłosi błąd.

0

Może to być dowolny typ, który może być użyty w kontekście boolowskim, a void * nie jest tutaj szczególny, prawda?

2

Każda integralna konwersja operator działałaby w ten sam sposób. Zwracasz 0 w swoim operatorze, stąd False.

operator [int,double,uint64_t,<any_integral>]() const 
{ 
    std::cout << "integral operator called" << std::endl; 
    return 0; 
} 

Dowolny typ integralny może być użyty w wyrażeniu logicznym.

4

To, co się naprawdę dzieje, polega na tym, że Twoja klasa ma niejawną konwersję na wskaźnik typu void* w tym przypadku. Zwracasz wartość 0, która jest makrem NULL, które jest akceptowane jako typ wskaźnika.

Wskaźniki mają domyślną konwersję na wartości binarne, a wskaźniki zerowe są przekształcane na wartości false.

Naprawdę można mieć inny niejawna konwersja do wskaźnika dla Foo:

operator int*() const 
{ 
    std::cout << "operator int* const" << std::endl; 
    return new int(3); 
} 

A twój wyjście zmieni się

operator int* const
True

Jednak jeśli masz zarówno, to dostaniesz Błąd kompilatora:

class Foo{ 
public: 

    operator int*() const 
    { 
     std::cout << "operator int* const" << std::endl; 
     return new int(3); 
    } 
    operator void*() const 
    { 
     std::cout << "operator void*() const" << std::endl; 
     return 0; 
    } 
}; 

main.cpp:26:9: error: conversion from 'Foo' to 'bool' is ambiguous

Jeśli jednak jawnie zdefiniować konwersję też bool, to nie jest niejednoznaczna:

operator void*() const 
{ 
    std::cout << "operator void*() const" << std::endl; 
    return 0; 
} 

operator bool() const 
{ 
    std::cout << "operator bool() const" << std::endl; 
    return true; 
} // <--- compiler chooses this one 

Temat implicit conversions całkiem interesujące, ponieważ odzwierciedla jak kompilator wybiera odpowiednią funkcję składową dla podanych argumentów (konwersja wartości, integralne promocje itp.).

Oznacza to, że kompilator ma listę priorytetów, którą wybierze, próbując określić, co masz na myśli. Jeśli dwa przeciążenia mają ten sam priorytet, pojawia się błąd.

Na przykład operator bool zawsze będzie wybrany, ale jeśli zamiast miał do wyboru operator int i operator void*, następnie operator int zostanie wybrany, ponieważ wybiera konwersję numerycznej nad wskaźnik konwersji.

Jednakże, jeśli masz zarówno operator int, jak i operator char, otrzymasz komunikat o błędzie, ponieważ oba są liczbowymi całkowitymi konwersjami.

+0

'operator int()' również działa, nie tylko wskaźniki. – xinaiz

+0

Dziękuję, dobra odpowiedź. Ale tak naprawdę nie odpowiada na pytanie, dlaczego C++ "spadnie" i spróbuje przekonwertować mój obiekt na dowolny typ wskaźnika? – jensa

+0

@jensa: Zobacz moje zmiany, aby uzyskać dokładniejsze wyjaśnienie. – AndyG

6

Przez kilka odwołań w normie:

  • §6.4.0.4 [stmt.select]

    The value of a condition that is an expression is the value of the expression, contextually converted to bool for statements other than switch

  • §4.0.4 [konwersji]

    Certain language constructs require that an expression be converted to a Boolean value. An expression e appearing in such a context is said to be contextually converted to bool and is well-formed if and only if the declaration bool t(e); is well-formed, for some invented temporary variable t .

  • §8.5.17 [dcl.init]

    The semantics of initializers are as follows. The destination type is the type of the object or reference being initialized and the source type is the type of the initializer expression.

  • §8.5.17.7 [dcl.init]

    Otherwise, if the source type is a (possibly cv-qualified) class type, conversion functions are considered. The applicable conversion functions are enumerated (13.3.1.5), and the best one is chosen through overload resolution (13.3). The user-defined conversion so selected is called to convert the initializer expression into the object being initialized. If the conversion cannot be done or is ambiguous, the initialization is ill-formed.

  • §13.3.1.5 [over.match.conv]

    Assuming that “cv1 T is the type of the object being initialized, and “cv S is the type of the initializer expression, with S a class type, the candidate functions are selected as follows:

    The conversion functions of S and its base classes are considered. Those non-explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T via a standard conversion sequence (13.3.3.1.1) are candidate functions. For direct-initialization, those explicit conversion functions that are not hidden within S and yield type T or a type that can be converted to type T with a qualification conversion (4.4) are also candidate functions.

  • §4.13.1 [conv.bool]

    A prvalue of arithmetic, unscoped enumeration, pointer, or pointer to member type can be converted to a prvalue of type bool . A zero value, null pointer value, or null member pointer value is converted to false ; any other value is converted to true .

Powiązane problemy