2012-06-02 16 views
5

Supponiamo che io abbia una funzione che esegue alcuni effetti collaterali e quindi restituisce una risposta:puntatori a funzione e conversioni di tipo di ritorno

int foo() 
{ 
    perform_some_side_effect(); 
    return 42; 
} 

voglio legare foo ad un puntatore a funzione, ma non sono interessato al risposta, solo l'effetto collaterale:

void (*bar)() = foo; 

Tuttavia, questo sembra essere un errore di tipo:

error: invalid conversion from ‘int (*)()’ to ‘void (*)()’ 

Qual è la logica dietro quell'errore? Perché il sistema di tipi non consente di ignorare la risposta?


Una nota a parte, funziona se mi avvolgo il puntatore a funzione in una std::function:

std::function<void()> baz = foo; 

Come si fa std::function (apparentemente) riescono ad aggirare questa restrizione nel sistema tipo?

+1

Il razionale è solo che sono diversi tipi. Puoi sovrascrivere questo (a tuo rischio e pericolo) con 'reinterpret_cast'. –

+0

@StevenBurnap: la domanda è * perché * non possono essere convertiti. È ovvio che sono tipi diversi. Anche un 'float' non è un' int, ma puoi convertirli. – Puppy

+0

Direi che è perché come regola generale non è permesso lanciare i puntatori. –

risposta

9

What is the rationale behind that error? Why doesn't the type system allow me to ignore the answer?

Il motivo è che i tipi sono diversi e il codice generato nel punto di chiamata (tramite il puntatore di funzione) è diverso. Si consideri una convenzione di chiamata in cui tutti gli argomenti sono scritti nello stack e lo spazio per il valore di ritorno è anche riservato nello stack. Se la chiamata passa attraverso lo void (*)(), lo spazio non verrà riservato nello stack per il valore restituito, ma la funzione (ignara di come viene chiamata) continuerà a scrivere lo 42 nella posizione in cui il chiamante deve avere lo spazio riservato.

How does std::function (apparently) manage to circumvent this restriction in the type system?

Non funziona. Crea un oggetto funzione che avvolge la chiamata alla funzione effettiva. Conterrà un membro del tipo:

void operator()() const { 
    foo(); 
} 

Ora, quando il compilatore elabora la chiamata a foo si sa cosa si deve fare per chiamata una funzione che restituisce un int e lo farà in base alla convenzione di chiamata . Poiché il modello non restituisce, ignorerà semplicemente il valore - che è stato effettivamente restituito.

+1

+1: risposta più completa. –

1

std::function deve essere solo compatibile con la sorgente, ovvero può generare una nuova classe che genera un nuovo codice di avviso che ignora il risultato. Il puntatore della funzione deve essere compatibile con i binari e non può fare quel lavoro - void(*)() e int(*)() punto allo stesso identico codice.

1

Si può pensare a std::function<> fare questo per il vostro caso particolare:

void __func_void() 
{ 
    foo(); 
} 

In realtà è un po 'più complicato di così, ma il punto è che genera il codice del modello insieme a tipo cancellazione di non preoccuparsi delle specifiche.

1

In aggiunta a ciò che altri hanno detto, il chiamante deve anche il tipo restituito per sapere quale distruttore dovrebbe invocare sul risultato (il valore restituito può essere temporaneo).


Purtroppo non è così facile come

auto (*bar)() = foo; 

Anche se GCC e Clang accettano questo. Devo ricontrollare le specifiche per vedere se è effettivamente corretto.

Update: le specifiche dice

The auto type-specifier signifies that the type of a variable being declared shall be deduced from its initializer or that a function declarator shall include a trailing-return-type.

Questo può essere fuorviante se letta veloce, ma questo è attuato da GCC e clang da applicare solo al dichiaratore toplevel. Nel nostro caso, questo è un dichiaratore di puntatore. Il dichiaratore annidato in esso è un dichiaratore di funzioni. Quindi sostituisci auto per void e il compilatore dedurrà il tipo per te.


A proposito, si può sempre fare questo lavoro manualmente, ma ci vuole un po 'di trucchi per farla funzionare

template<typename FunctionType> 
struct Params; 

template<typename ...Params> 
struct Params<void(Params...)> { 
    template<typename T> 
    using Identity = T; 

    template<typename R> 
    static Identity<R(Params...)> *get(R f(Params...)) { 
    return f; 
    } 
}; 

// now it's easy 
auto bar = Params<void()>::get(foo); 
+0

Anche questo compila in VS2011 RC. auto bar = Test; \t bar(); – Jagannath

Problemi correlati