Имейте и совокупную инициализацию и дедукцию шаблона - PullRequest
0 голосов
/ 04 ноября 2018

Справочная информация:

C ++ 17 обладает двумя замечательными функциями: агрегатная инициализация и удержание типа шаблона (для классов). Агрегированная инициализация позволяет создавать экземпляры полей, не копируя и не перемещая их, а вычет типа шаблона делает его таким, что вам не нужно указывать тип вашего аргумента.

Класс Wrapper в приведенном ниже коде является примером этого. Пока HAVE_MOVE_AND_COPY не определено, он имеет агрегатную инициализацию, но тогда вывод типа шаблона не работает.

С другой стороны, если HAVE_MOVE_AND_COPY определено , то вычитание типа шаблона работает, но агрегатная инициализация прерывается. Как я могу иметь оба?

#include <cstdio>
#include <utility>

template<class T> 
struct Wrapper {
    T value;
   #ifdef HAVE_MOVE_AND_COPY
    Wrapper(T const & val) : value{val} {}
    Wrapper(T && val) : value{std::move(val)} {}
   #endif
    Wrapper(Wrapper const &) = default;
    Wrapper(Wrapper &&) = default;


};

struct VocalClass {
    VocalClass() { puts("VocalClass()"); }
    VocalClass(VocalClass const&) { puts("VocalClass(VocalClass const &)"); }
    VocalClass(VocalClass &&) { puts("VocalClass(VocalClass &&)"); }
};

int main() {
    Wrapper<VocalClass> w { VocalClass() }; 

   #ifdef TRY_DEDUCTION
    Wrapper w2 { VocalClass() }; 
   #endif
}

Пример:

Не происходит перемещение или копирование, но у вас нет вычета шаблона:

$ c++ -std=c++17 example.cc && ./a.out
VocalClass()

Имеет шаблонный вычет, но VocalClass перемещается:

$ c++ -DHAVE_MOVE_AND_COPY -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out 
VocalClass()
VocalClass(VocalClass &&)
VocalClass()
VocalClass(VocalClass &&)

Без HAVE_MOVE_AND_COPY, разрывы вычета типа шаблона:

sky@sunrise:~$ c++ -DTRY_DEDUCTION -std=c++17 example.cc && ./a.out 
example.cc: In function ‘int main()’:
example.cc:27:31: error: class template argument deduction failed:
     Wrapper w2 { VocalClass() };
                               ^
example.cc:27:31: error: no matching function for call to ‘Wrapper(VocalClass)’
example.cc:12:5: note: candidate: ‘template<class T> Wrapper(Wrapper<T>&&)-> Wrapper<T>’
     Wrapper(Wrapper &&) = default;
     ^~~~~~~
example.cc:12:5: note:   template argument deduction/substitution failed:
example.cc:27:31: note:   ‘VocalClass’ is not derived from ‘Wrapper<T>’
     Wrapper w2 { VocalClass() };
                               ^
example.cc:11:5: note: candidate: ‘template<class T> Wrapper(const Wrapper<T>&)-> Wrapper<T>’
     Wrapper(Wrapper const &) = default;
     ^~~~~~~
example.cc:11:5: note:   template argument deduction/substitution failed:
example.cc:27:31: note:   ‘VocalClass’ is not derived from ‘const Wrapper<T>’
     Wrapper w2 { VocalClass() };
                               ^
example.cc:5:8: note: candidate: ‘template<class T> Wrapper(Wrapper<T>)-> Wrapper<T>’
 struct Wrapper {
        ^~~~~~~
example.cc:5:8: note:   template argument deduction/substitution failed:
example.cc:27:31: note:   ‘VocalClass’ is not derived from ‘Wrapper<T>’
     Wrapper w2 { VocalClass() };

Вопрос

Можно ли как-нибудь удержать тип шаблона и инициализацию агрегата?

Ответы [ 2 ]

0 голосов
/ 04 ноября 2018

Вы неправильно понимаете, что означает термин "совокупный".

Во-первых, то, что вы делаете, называется инициализация списка. Инициализация списка будет только агрегировать-инициализировать ваш экземпляр, если тип экземпляра является агрегатным. Агрегатная инициализация позволяет вам инициализировать базовые классы и / или члены вашего класса в порядке списка инициализаторов.

От cppreference :

Агрегат относится к одному из следующих типов:

  • тип массива
  • тип класса (обычно struct или union), который имеет
    • нет пользовательских, унаследованных или явных конструкторов (допускаются явно заданные по умолчанию или удаленные конструкторы)
  • нет виртуальных, частных или защищенных (начиная с C ++ 17) базовых классов
  • нет виртуальных функций-членов
  • нет инициализаторов элементов по умолчанию

Здесь применяется второй пункт. Поскольку у вас есть предоставленный пользователем конструктор (конструктор, записанный пользователем, а не сгенерированный компилятором), когда определено HAVE_MOVE_AND_COPY, ваш тип не является агрегатом, и компилятор будет искать только конструкторы для инициализации вашего экземпляра.

Барри покрывает остальное о том, как сделать агрегат с вычетом аргумента шаблона класса.

0 голосов
/ 04 ноября 2018

Во-первых, термин «вычет аргумента шаблона класса».

Во-вторых, вам нужно руководство по удержанию :

template<class T> 
struct Wrapper {
    T value;
};

template <typename T>
Wrapper(T) -> Wrapper<T>; // this is a deduction guide

Без конструктора вам нужен какой-то другой способ управления выводом. Вот для чего это нужно, и это позволяет:

Wrapper w{4}; // ok, Wrapper<int>
...