2017-10-03 22 views
6

Rozważmy następujący kod:Czy mogę uniknąć wyraźnego zapisu konstruktora dla każdej struktury w wariancie std ::?

#include <variant> 

struct x { 
    int y; 
}; 

int main() { 
    std::variant<x> v(std::in_place_type<x>, {3}); /*1*/ 
    return std::get<x>(v).y; 
} 

nie skompilować i nie robi podczas wyjmowania {} z linii /*1*/, choć łączna inicjalizacji

x a{3}; 
x b({3}); 

działa w obu formach „konstruktora-like”. Czy mogę w jakiś sposób uświadomić inicjatorowi std::variant możliwości konstruowania struktur przy użyciu agregacyjnej inicjalizacji bez konieczności pisania nudnych konstruktorów dla każdej struktury, które mogą być używane w moim prawdziwym świecie?

Spodziewam się to do pracy, w jakiś sposób, jak za cppreference dwa przeciążenia (5) i (6) w kwestii zarówno powiedzieć

konstruuje wariant z określoną alternatywę T i inicjuje zawarty wartość z argumentami [...]

Używam GCC 7 jeśli liczy.

+0

Jeśli konsola, 'std :: make_unique' i rodzina mają takie samo ograniczenie :( – Rakete1111

+0

Inicjalizacja agregacji zawsze była cierniem w systemie inicjującym C++. –

Odpowiedz

1

Nie ma rozwiązania tego problemu, oprócz dodania konstruktora. Standardowe mandaty to dla obu przeciążeniem można wymienić, [variant.ctor]19 i [variant.ctor]23 odpowiednio:

Efekty: Inicjuje zawartej wartość jakby bezpośrednim non-lista-Inicjowanie obiektu typu T z argumentami std​::​forward<Args>(args)....

Efekty: Inicjuje zawartej wartość jakby bezpośrednim non-lista-Inicjowanie obiektu typu T z argumentami il, std​::​forward<Args>(args)....

zawsze można skopiować lub przenieść obiekt przy użyciu:

std::variant<x> v(std::in_place_type<x>, x{3}); 
// or more clear and does the same thing 
std::variant<x> v(x{3}); 
3

Może to nie jest dokładnie to, o co prosisz, ale co z jawnym konstruowaniem obiektu zamiast polegania na wnioskach typu?

#include <variant> 

struct x { 
    int y; 
}; 

int main() { 
    std::variant<x> v(std::in_place_type<x>, x{3}); 
    return std::get<x>(v).y; 
} 
+0

Właściwie byłbym z tego zadowolony!Moje pytanie, na tytuł, dotyczy unikania konstruktora, który to osiąga. Technicznie wiąże się to z poruszaniem się, ale w prostych przypadkach optymalizator zadba o to bez żadnego narzutu. –

+0

Rzeczywiście byłoby to proste, jeśli twoja klasa dostarcza ruchomy konstruktor. Ponieważ nie chcesz żadnego jawnie zdefiniowanego konstruktora, powinieneś zagwarantować niejawny ruchomy konstruktor (lub przynajmniej jeden domyślny). – cbuchart

0

Jeśli chcesz iść przesadą, możemy utworzyć typ fabryki, która operator konwersji:

template <class... Args> 
struct list_init_from { 
    std::tuple<Args...> args; 

    template <class T> 
    operator T() { 
     return std::apply([](auto... args){ 
      return T{args...}; 
     }, args); 
    } 
}; 

template <class... Args> 
list_init_from(Args...) -> list_init_from<Args...>; 

Która możesz użyć:

std::variant<x> v(std::in_place_type<x>, list_init_from{3}); 

To działa, ale pozostawia mu ch do życzenia: perfekcyjne przekazywanie, SFINAE na operatorze konwersji i jednoznaczne określenie, które typy umożliwiają konwersje to ćwiczenia pozostawione czytelnikowi.

Powiązane problemy