Nasconde la classe base vuota per l'inizializzazione aggregata


9

Considera il seguente codice:

struct A
{
    // No data members
    //...
};

template<typename T, size_t N>
struct B : A
{
    T data[N];
}

È così che devi inizializzare B: B<int, 3> b = { {}, {1, 2, 3} }; voglio evitare il vuoto {} inutile per la classe base. C'è una soluzione proposta da Jarod42 qui , tuttavia, non funziona con l'inizializzazione di default degli elementi: B<int, 3> b = {1, 2, 3};va bene ma B<int, 3> b = {1};non lo è: b.data[1]e b.data[2]non sono inizializzati di default su 0 e si verifica un errore del compilatore. Esiste un modo (o ci sarà con c ++ 20) per "nascondere" la classe base dalla costruzione?


2
Perché non aggiungere un costruttore template<class... Ts> B(Ts... args) : data{args...} {}?
Evg

Perché è un commento? Sembra funzionare, lol
user7769147,

Questa è una soluzione così ovvia che ho pensato che avessi qualche motivo per non usarla. :)
Evg

Era troppo facile xD. Se lo scrivi come risposta, lo accetterò
user7769147,

Risposte:


6

La soluzione più semplice è aggiungere un costruttore variadico:

struct A { };

template<typename T, std::size_t N>
struct B : A {
    template<class... Ts, typename = std::enable_if_t<
        (std::is_convertible_v<Ts, T> && ...)>>
    B(Ts&&... args) : data{std::forward<Ts>(args)...} {}

    T data[N];
};

void foo() {
    B<int, 3> b1 = {1, 2, 3};
    B<int, 3> b2 = {1};
}

Se si fornisce un numero inferiore di elementi {...}nell'elenco di inizializzazione di N, gli elementi rimanenti nell'array dataverranno inizializzati come da T().


3
Ho appena scoperto perché questo è diverso dall'inizializzazione aggregata. Se consideri B<Class, 5> b = {Class()}; Classche verrà costruito prima e poi spostato, mentre usando l'inizializzazione aggregata Classverrebbe costruito, nessuna mossa coinvolta
user7769147

@ user7769147, buon punto. Puoi prendere std::tupledegli argomenti e usarli per costruire oggetti sul posto. Ma la sintassi sarà piuttosto ingombrante.
Evg

1
Ho trovato casualmente una soluzione che risolve questo problema, lo lascerò come risposta accettata per ringraziarti per la tua disponibilità :).
user7769147,

4

Poiché C ++ 20 si potrebbe utilizzare initializers designato in inizializzazione aggregata .

B<int, 3> b = { .data {1} }; // initialize b.data with {1}, 
                             // b.data[0] is 1, b.data[1] and b.data[2] would be 0

Questo è ancora troppo prolisso per me, questo è stato un esempio minimo. Il mio membro dell'array ha uno strano nome che dovrebbe essere ignorato dall'utente
user7769147

4

Sempre con il costruttore, potresti fare qualcosa del tipo:

template<typename T, size_t N>
struct B : A
{
public:
    constexpr B() : data{} {}

    template <typename ... Ts,
              std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
                               || !std::is_same_v<B, std::decay_t<T>>, int> = 0>
    constexpr B(T&& arg, Ts&&... args) : data{std::forward<T>(arg), std::forward<Ts>(args)...}
    {}

    T data[N];
};

dimostrazione

SFINAE è fatto principalmente per evitare di creare un pseudo costruttore di copie B(B&).

Avresti bisogno di un tag privato extra per supportare B<std::index_sequence<0, 1>, 42>;-)


Perché avete bisogno ((void)Is, T())...? E se semplicemente lo omettessi? Gli elementi rimanenti non verranno inizializzati T()per impostazione predefinita?
Evg

1
@Evg: anzi, semplificato. Avevo paura di inizializzare solo gli elementi rimanenti invece di inizializzarli con il valore ...
Jarod42,

2

Ho trovato un'altra soluzione che (non so come) funziona perfettamente e risolve il problema di cui stavamo discutendo sotto la risposta di Evg

struct A {};

template<typename T, size_t N>
struct B_data
{
    T data[N];
};

template<typename T, size_t N>
struct B : B_data<T, N>, A
{
    // ...
};

Soluzione interessante. Ma ora bisogna usare this->datao using B_data::data;accedere dataall'interno B.
Evg
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.