Это фигурные скобки — признак конструктивного типа

#c #c 11 #stl #typetraits #curly-braces

#c #повышение #признаки типов #boost-вариант

Вопрос:

Как я могу проверить, можно ли сконструировать конкретный тип typename T из аргументов typename ...Args таким образом T{Args...} ? Я знаю о std::is_constructible< T, Args... > черте типа из <type_traits> , но он работает со скобками, а не с фигурными скобками. У меня не слишком много опыта в написании признаков типа, поэтому я не могу привести первоначальный пример. В качестве упрощения мы можем принять любые разумные утверждения, даже если это приводит к не слишком значительной потере общности.

Комментарии:

1. @chris но как насчет struct A { std::set< int > a; int b; }; using variant_type = boost::variant< int, double, A >; вариантного типа? std::initializer_list{std::decltype< std::set< int > >(), int()} не имеет смысла.

2. std::is_constructable<T,initializer_list<K>> если все аргументы имеют один и тот же тип….

Ответ №1:

 template<class T, typename... Args>
decltype(void(T{std::declval<Args>()...}), std::true_type())
test(int);

template<class T, typename... Args>
std::false_type
test(...);

template<class T, typename... Args>
struct is_braces_constructible : decltype(test<T, Args...>(0))
{
};
 

Комментарии:

1. Может ли это быть выражено в терминах структур / классов, а не в терминах типов возвращаемых функций?

2. Возможно, вам это больше не понадобится, но ответ — да; Я воспользовался приоритетом кандидата template<class... S> struct Satisfied<S..., decltype(void(E))>: std::true_type по умолчанию template<class... S, class=void> struct Satisfied: std::false_type . (E должно быть вашим выражением {S …} например S0{std::declval<S>()...} , вы поняли идею.) Позже вы можете специализироваться на некоторых f(...) -> std::enable_if_t<Satisfied<...>::value, ...> и f(...) -> std::enable_if_t<!Satisfied<...>::value, ...> без двусмысленности (ровно один кандидат после SFINAE.)

Ответ №2:

Мое решение на основе класса SFINAE:

 #include <type_traits>


template< typename ...types >
struct identity
{

};

template< typename ...types >
struct void_t
{

    using type = void;

};

template< typename type, typename identity, typename = void >
struct is_embraceable
        : std::false_type
{

};

template< typename type, typename ...arguments >
struct is_embraceable< type, identity< arguments... >, void_t< decltype(type{std::declval< arguments >()...}) > >
        : std::true_type
{

};

template< typename type, typename ...arguments >
constexpr bool is_embraceable_v = is_embraceable< type, identity< arguments... > >::value;
 

Ответ №3:

в c 20 вы можете использовать выражения Requires:

 template< class T, class ...Args >
inline constexpr bool is_brace_constructible_v = requires {T{std::declval<Args>()...};};
 

Комментарии:

1. Или с полной концепцией: template<имя_типа T, имя_типа … TArgs> concept braces_constructible = требуется { T{std::declval<TArgs>()…}; }; шаблон <braces_constructible T, класс… TArgs> встроенный constexpr bool is_braces_constructible_v = braces_constructible<T, TArgs …>;

Ответ №4:

C также позволяет инициализировать ссылки в фигурных скобках:

 struct dummy{};
dummy a;
dummy amp; b{a};
dummy c{b};

dummy constamp; d{a};
dummy e{d};
 

Но is_braces_constructible из ответа Simple возвращается std::false_type для ссылок, инициализированных фигурными скобками, с помощью GCC8, но работает, например, с Clang 7. В ИСХОДНОМ ответе Orient также не удается скомпилировать инициализации ссылочных скобок с помощью GCC8. Его ОТРЕДАКТИРОВАННЫЙ ответ даже возвращается std::false_type в гораздо большем количестве случаев как с GCC8, так и с Clang 7.

Вот ответ C 17, который (надеюсь) работает и для всех случаев с GCC8:

 #include <type_traits>

#if __GNUC__

template<
    typename T,
    typename std::enable_if_t<
        std::is_lvalue_reference<T>::value
    >* = nullptr
>
constexpr void gcc_workaround_braces_initialize_refs(Tamp; t);

template<typename T>
constexpr void gcc_workaround_braces_initialize_refs(T constamp; t);

template<
    typename T,
    typename std::enable_if_t<
        !std::is_lvalue_reference<T>::value
    >* = nullptr
>
constexpr Tamp;amp; gcc_workaround_braces_initialize_refs(Tamp;amp; t);

template< typename T, typename Identity, typename = std::void_t<>>
struct is_braces_constructible_impl : std::false_type {};

template< typename T, typename ...Args >
struct is_braces_constructible_impl<
    T,
    std::tuple< Args... >,
    std::void_t< decltype(gcc_workaround_braces_initialize_refs<T>({std::declval< Args >()...}))>
> : std::true_type {};

#else // !__GNUC__

template< typename T, typename Identity, typename = std::void_t<>>
struct is_braces_constructible_impl : std::false_type {};

template< typename T, typename ...Args >
struct is_braces_constructible_impl<
    T,
    std::tuple< Args... >,
    std::void_t< decltype(T{std::declval< Args >()...}) >
> : std::true_type {};

#endif // !__GNUC__

template<class T, typename... Args>
struct is_braces_constructible {
    using type = is_braces_constructible_impl< T, std::tuple< Args... > >;
    static inline constexpr bool value = type::value;
};
 

Живой код