2015-03-05 14 views
6

ho una tale classe di utilità:inizializzazione e lambda-tipo di argomento

struct Atreturn 
{ 
    std::function<void()> funcdestr; 
    Atreturn(std::function<void()> fd): funcdestr(fd) {} 
    ~Atreturn() { funcdestr(); } 
}; 

Nota n explicit attributo alla costruttore.

Possibile uso dovrebbe essere:

  1. diretto inizializzazione costruttore di chiamata:

    Atreturn hook ([something]() { DestroySomething(something); }); 
    
  2. Copy-inizializzazione costruttore di chiamata:

    Atreturn hook = [something]() { DestroySomething(something); }; 
    
  3. diretto-list-inizializzazione chiamata al costruttore:

    Atreturn hook { [something]() { DestroySomething(something); }}; 
    

Ora la domanda: al mio migliore conoscenza, il metodo # 1 e # 2 dovrebbe essere consentito in quanto sono in teoria la stessa cosa, a condizione che non c'è il explicit presso il costruttore, mentre # 3 non dovrebbe essere permesso perché questa sintassi impedisce le conversioni (almeno lo è per lo int se si è provato a int{2.1}).

Tuttavia, gcc 4.9 consente il metodo n. 1 e n. 3, ma non n. 2 (e dice conversion from '...::<lambda()>' to non-scalar 'Atreturn' type requested). Sembra pazzesco perché normalmente accade solo se hai il costruttore explicit. Qualcuno può spiegare, perché?

Inoltre, permettimi di rendere questo problema più esplicito: ho bisogno di una sintassi non troppo maldestra per inizializzare questo oggetto Atreturn, almeno senza bisogno di parentesi o parentesi extra. Il problema è che gli editor con la funzione auto-indent hanno problemi con la corretta reindentazione quando l'argomento è C++ 11 lambda. Quindi ho bisogno di un po 'di sintassi che potrebbe essere espresso come:

Atreturn BLAH BLAH BLAH [something]() { DestroySomething(something); }; 
+1

(Aggiungere anche la costruzione di inizializzazione della copia-lista: 'Areturn hook = {...}'?) –

+0

È lo stesso metodo # 3, this = è facoltativo in C++ 11. – Ethouris

+0

# 1 e # 2 sono * non * uguali. Lo sarebbero se il tipo a destra fosse 'Atreturn', ma non lo è. – Angew

risposta

7

mentre il 3 # non dovrebbe essere consentito perché questa sintassi impedisce conversioni (almeno è così per int se si è tentato int {2,1}).

Non è giusto. La regola è che il restringimento delle conversioni non è consentito. Sono ammessi altri tipi di conversioni. int{2.1} è una conversione restringente, perché modifica il valore, perdendo precisione. int{2.0} non è una conversione di restringimento, perché il valore non viene modificato.

Il motivo # 2 non riesce è che richiede due conversioni implicite definite dall'utente, il che è vietato.

Concettualmente, una copia inizializzazione quali:

Atreturn hook = []() {}; 

è equivalente a:

Atreturn hook = Atreturn([]() {}); 

(tranne che non può chiamare costruttori 'esplicite', e il compilatore è permesso di elidere il copia).

Ciò significa che in primo luogo il lambda dovrebbe convertirsi implicitamente in function<void()> e che quindi sarebbe implicitamente convertito in Atreturn. Entrambe queste conversioni sono una "sequenza di conversione definita dall'utente", ovvero chiamano un costruttore, piuttosto che conversioni incorporate come int a long e lo standard dice che una sequenza di conversione implicita non può includere più di una conversione definita dall'utente.

Il problema è in realtà estranei a lambda, è possibile dimostrare esattamente lo stesso errore come questo:

struct L { }; 
struct F { F(L) { } }; 
struct A { A(F) { } }; 
A a = L(); 

l.cc:4:9: error: conversion from ‘L’ to non-scalar type ‘A’ requested 
A a = L(); 
     ^

Nuovamente, il problema è che la sequenza conversione implicita L -> F -> A coinvolge due conversioni definite dall'utente, che è proibito .

Non ho molta simpatia per il tuo problema di voler adattare il codice per aiutare l'indentazione automatica - il codice non deve essere alterato per adattarsi a un editor imperfetto. Tuttavia, un'altra opzione potrebbe essere quella di aggiungere un costruttore di template che accetta tutto ciò che può essere convertito in std::function<void()>, ad es.

struct Atreturn 
{ 
    using func_type = std::function<void()>; 
    template<typename T, 
      typename Requires = decltype(func_type(std::declval<T&&>())> 
    Atreturn(T t) : funcdestr(std::move(t)) { } 
    ... 
}; 

Questo permetterà la lambda da convertire direttamente Atreturn, senza richiedere una conversione implicita per function<void()> prima.

+0

"Lambda to' function 'è abbastanza ok, ma dove vedi la necessità di convertirlo in' Atreturn'? Non dovrebbe l'inizializzazione con il segno di uguale utilizzare il costruttore 1-argomento con parametro di quel tipo? – Ethouris

+3

@Ethouris No, l'inizializzazione diretta (no '=') e l'inizializzazione della copia (con '=') sono * non * uguali. Quest'ultimo richiede la creazione di un temporaneo e l'utilizzo come argomento per il copione (anche se ciò è elidato in pratica). – Angew

+0

@Ethouris, ho aggiornato la risposta per spiegarlo. Sul lato destro di '=' le regole del linguaggio dicono che esiste un oggetto temporaneo 'Atreturn'. Anche se questo temporaneo viene eliminato, le regole sull'overloading e sulle conversioni devono comportarsi come se il temporaneo fosse stato creato. –

Problemi correlati