Элемент шаблона класса инициализации (тип агрегата) с инициализатором агрегата, но без лишних скобок - PullRequest
0 голосов
/ 10 ноября 2018

Имея этот код:

struct Vec3 {
    int x;
    int y;
    int z;
};

template <typename T>
class myProperty {
public:
   myProperty(const T& initValue) : m_value{initValue} {}
private:
    T m_value;
};

При создании myProperty тип объекта:

myProperty<int> ip{1};
myProperty<Vec3> vp1{{1, 2, 3}};
// myProperty<Vec3> vp2{1, 2, 3}; ERROR: myProperty doesn't have a matching constructor.

Есть ли элегантный способ заставить vp2 работать с инициализацией? Специализация myProperty для Vec3 является излишним.

1 Ответ

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

Простое решение заключается в использовании конструктора шаблонов с переменными параметрами:

template <typename ...P> myProperty(P &&... p) : m_value{std::forward<P>(p)...} {}

Компиляция myProperty<Vec3> vp2{1, 2, 3};.

Также он останавливает myProperty<Vec3> vp1{{1, 2, 3}}; от компиляции (что, кажется, соответствует вашим намерениям).

Проблема с этим параметром заключается в том, что он препятствует правильной работе конструкции копирования. (Если параметр является неконстантным myProperty<T> lvalue, тогда этот конструктор с переменными параметрами лучше соответствует myProperty(const myProperty &).)

Это можно решить с помощью SFINAE:

C ++ 17 с <experimental/type_traits>:

#include <experimental/type_traits>
#include <utility>

template <typename T, typename ...P> using list_constructible = decltype(T{std::declval<P>()...});

// ...

template
<
    typename ...P,
    typename = std::enable_if_t<std::experimental::is_detected_v<list_constructible, T, P...>>
>
myProperty(P &&... p) : m_value{std::forward<P>(p)...} {}

C ++ 14:

#include <type_traits>
#include <utility>

template <typename...> using void_t = void;
template <typename DummyVoid, template <typename...> class A, typename ...B> struct is_detected : std::false_type {};
template <template <typename...> class A, typename ...B> struct is_detected<void_t<A<B...>>, A, B...> : std::true_type {};
template <typename T, typename ...P> using list_constructible = decltype(T{std::declval<P>()...});

// ...

template
<
    typename ...P,
    typename = std::enable_if_t<is_detected<void, list_constructible, T, P...>::value>
>
myProperty(P &&... p) : m_value{std::forward<P>(p)...} {}
...