2013-02-26 15 views
5

Stavo leggendo il std::thread documentation at cppreference (non sempre accurato al 100%, lo so) e ho notato la seguente definizione per il comportamento di std::thread quando è passato un "pointer-to-data-member" (non "puntatore-a-membro-funzione") come primo parametro (f) e un oggetto della classe richiesta come secondo argomento (t1 dopo la copia locale del thread-storage):std :: thread con puntatore al membro dati

Se N == 1 e f è puntatore a un oggetto dati membro di una classe, quindi è accessibile. Il valore dell'oggetto è ignorato. Effettivamente, viene eseguito il seguente codice: t1. * F se e il tipo di t1 è T, riferimento a T o riferimento a tipo derivato da T. (* t1). * F altrimenti.

Ora, non ho intenzione di utilizzare std::thread in questo modo, ma sono sconcertato da questa definizione. Apparentemente, l'unica cosa che succede è che si accede al membro dati e il valore viene ignorato, il che non sembra che possa avere effetti collaterali osservabili, il che significa (per quanto posso dire) che potrebbe anche essere un no-op. (Potrei mancare qualcosa di ovvio ...?)

In un primo momento, ho pensato che si trattasse di un errore di stampa e intendevo dire che il membro dati è stato richiamato e quindi chiamato (poiché potrebbe essere un oggetto chiamabile, anche se non è una funzione), ma ho provato con il seguente codice nel GCC-4.7 e in effetti non v'è alcuna chiamata:

#include <iostream> 
#include <thread> 

struct S 
{ 
    void f() { 
     std::cout << "Calling f()" << std::endl; 
    } 

    struct { 
     void operator()() { 
      std::cout << "Calling g()" << std::endl; 
     } 
    } g; 
}; 

int main(int, char**) 
{ 
    S s; 
    s.f(); // prints "Calling f()" 
    s.g(); // prints "Calling g()" 

    std::cout << "----" << std::endl; 

    auto x = &S::f; // ptr-to-mem-func 
    auto y = &S::g; // ptr-to-data-mem 

    (s.*x)(); // prints "Calling f()" 
    (s.*y)(); // prints "Calling g()" 

    std::cout << "----" << std::endl; 

    std::thread t(x, &s); 
    t.join(); 
    // "Calling f()" printed by now 
    std::thread u(y, &s); 
    u.join(); 
    // "Calling g()" not printed 

    return 0; 
} 

c'è uno scopo a questa definizione, che non sembra realizzare qualcosa? Perché invece non fare passare un atto "pointer-to-data-member-callable" come una "funzione pointer-to-member" e rendere un errore "pointer-to-data-member-nonclable" un errore? In effetti, sembra che questo sarebbe il modo più semplice per implementarlo, dal momento che chiamare un "pointer-to-data-member-callable" ha una sintassi equivalente a chiamare "pointer-to-member-function" in altri contesti (a meno che non ci sia qualcosa nelle vaghezze della specializzazione dei template e delle regole di SFINAE che rende difficile trattarli in modo equivalente ...?)

Questo non è qualcosa di cui ho bisogno per il codice reale, ma il fatto che questa definizione esista mi fa sospettare che Mi manca qualcosa di fondamentale, che mi preoccupa ... qualcuno può illuminarmi di questo?

+0

+1 Questa formulazione deriva dalla definizione di "INVOKE", che è riportata in 20.8.2 della norma. C'è una domanda con lo stesso intento del tuo in particolare su questa formulazione nella definizione "INVOKE": http://stackoverflow.com/questions/12638393/why-does-invoke-facility-in-the-c11-standard-refer -to-data-members – jogojapan

risposta

3

Ciò è dovuto alla struttura di collegamento generica in base alla quale lo standard C++ 11 definisce non solo il modo in cui viene avviato un thread, ma anche il modo in cui funzionano std::bind e std::function.

Infatti, Paragrafo 30.3.1.2/3 del C++ 11 standard specifica circa il costruttore variadic di classe std::thread:

template <class F, class ...Args> explicit thread(F&& f, Args&&... args);

Effetti: Costruisce un oggetto di tipo filetto . Il nuovo thread di esecuzione viene eseguito INVOKE (DECAY_- COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) con le chiamate a DECAY_COPY in corso di valutazione nel thread di costruzione. Qualsiasi valore di ritorno da questa chiamata è ignorato. [...]

Ignorare cosa fa DECAY_COPY (non è rilevante per la domanda), questo è come il paragrafo 20.8.2 definisce il INVOKE pseudo-funzione:

Definire INVOKE (f, t1, t2, ..., tn) come segue: - (. T1 * f)

(t2, .. ., tN) quando f è un puntatore a una funzione membro di una classe T e t1 è un oggetto di tipo T o un riferimento a un oggetto di tipo T o un riferimento a un oggetto di un tipo derivato da T;

- ((* t1). * F) (t2, ..., tN) quando f è un puntatore a una funzione membro di una classe T e t1 non è uno di i tipi descritti nella voce precedente ;

- t1. * F quando N == 1 ef è un puntatore ai dati membro di una classe T e t1 è un oggetto di tipo T o un riferimento a un oggetto di tipo T o un riferimento a un oggetto di un tipo derivato da T;

- (* t1). * F quando N == 1 e f è un puntatore ai dati membro di una classe T e t1 non è uno dei tipi descritto nella voce precedente;

- f (t1, t2, ..., tN) in tutti gli altri casi.

Ora la domanda diventa:

Perché il C++ 11 standard definisce l'impianto INVOKE in quel modo?

E la risposta è here.

+1

Grazie, capisco da dove arriverà il comportamento da adesso ma non seguo ancora perché ... perché esattamente definirebbe il legame ai dati dei membri in quel modo? Solo così puoi recuperare un membro dati usando std :: mem_fn? Se legassi f = std :: mem_fn (& X :: n) e poi chiamassi f (& x), mi aspetterei che un errore o xn(), non xn ... potrebbe essere un caso d'uso valido per l'associazione ai dati dei membri in questo modo, ma perché non attraverso qualcosa chiamato std :: mem_data? –

+0

@StephenLin: Pur comprendendo il tuo punto, credo che SO non sia adatto per domande del tipo "* Perché le cose sono come sono, piuttosto che altrimenti? *". Ho provato a rispondere alla parte che potrebbe essere obiettivamente risposta. Speriamo che migliori risposte saranno date. –

+0

Nessun problema, la mia unica ragione per chiedere è perché ti sei collegato a quella risposta come spiegazione, che non sembra spiegare nulla (a parte che qualcuno potrebbe trovare utile questo comportamento sovraccarico ...) –

Problemi correlati