2016-07-22 61 views
16

問題:隱式bool轉換總是​​回退到試圖隱式轉換爲void*? (如果這種類型的轉換功能存在)。如果是這樣,爲什麼?C++,bool轉換總是​​回退到隱式轉換爲void *?

考慮下面的短程序:

#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; 
} 

該程序的輸出是:

operator void*() const 
False 

含義,變換函數到void*被調用。 如果我們在轉換函數前標記了explicit限定符,則隱式轉換爲void*將失敗。

編輯: 看來很多答案都是「空指針可以轉換爲false」。我理解這一點,我的問題是關於「如果我不能直接調用operator bool(),那麼我會嘗試轉換爲任何指針」。

+0

「將嘗試轉換爲任何指針」 - 'void'指針不「任何「指針。 'void'指針是'void'指針,沒有別的。它只接受任何類型的地址。 – xinaiz

+1

@BlackMoses我的意思是任何指針類型,在接受的答案中的確如此。 – jensa

+0

@JesperJuhl這不是一個愚蠢的問題。它詢問是否允許編譯器這樣做,以及它背後的推理。這對你來說可能相當明顯,但對於初學者到中級開發者來說,這可能會讓人困惑。 –

回答

15

如果編譯器不能在用戶定義的類型轉換爲bool直接,那麼它會嘗試做間接的,即轉換(通過用戶定義的轉換)的類型可以轉換到bool而不涉及另一用戶定義轉換。這種類型的列表中包括(並似乎限於)下列類型的:

  • 的整數算術類型(charint等)
  • 一個浮點運算類型(floatdoublelong double
  • 指針類型(void*屬於這裏,但它可能也是const std::vector<Something>*
  • 指針功能(包括一個指針指向一個成員函數)然而
  • 引用類型任何上述

注意,僅一個這樣的間接轉換必須存在。如果上述列表中的兩次或多次轉換是可能的,那麼編譯器將面臨不明確的情況並將報告錯誤。

0

這可能是任何可以在布爾上下文中使用的類型,而void *在這裏並不特別,呃?

2

任何積分轉換operator將以相同的方式工作。您在運營商中返回0,因此False

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

可以在邏輯表達式中使用任何整數類型。

4

真正發生的是,在這種情況下,你的類有一個隱式轉換爲指針類型void*。您返回0,這是NULL宏,它被接受爲指針類型。

指針有隱式轉換爲布爾值,空指針轉換爲false。

真的,你可以有不同的隱式轉換指針Foo

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

而且你的輸出就會變成

operator int* const
True

但是,如果你有,那麼你會得到一個編譯器錯誤:

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

但是,如果你明確地定義轉換,太布爾的話,那一點也不含糊:

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 

implicit conversions的話題其實挺有意思的,因爲它反映了編譯器如何選擇適當的成員函數對於給定的參數(價值轉換,積分促銷等)。

也就是說,編譯器有一個優先級列表,它會在嘗試確定你的意思時選擇。如果兩個重載具有相同的優先級,則會出現錯誤。

例如,operator bool總是會選擇,但如果你不是已經從operator intoperator void*選擇,然後operator int將被選中,是因爲它選擇了指針轉換數值轉換。

但是,如果你有operator intoperator char,那麼你會得到一個錯誤,因爲它們都是數字整數轉換。

+0

'operator int()'也可以工作,不僅是指針。 – xinaiz

+0

謝謝,很好的回答。但它並沒有真正回答我爲什麼C++會「回退」並嘗試將我的對象轉換爲任何指針類型的問題? – jensa

+0

@jensa:查看我的編輯以獲得更全面的解釋。 – AndyG

6

在標準的幾個參考文獻:

  • §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[CONV]

    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 .