make_array (a-la make_pair)
От: Skorodum Россия  
Дата: 02.03.15 10:21
Оценка: 1 (1)
Интересный код который позволяет создавать std::array в стиле std::make_pair и писать так:

#include <iostream>
 
int main()
{
    auto ch = 'a';
    auto const d = 65l;
    auto a1 = make_array(ch, d, 0);
    static_assert(std::is_same<decltype(a1)::value_type, long>(), "");
    std::cout << a1[0] << std::endl;
 
    constexpr auto a2 = to_array("abc");
    static_assert(std::is_same<decltype(a2)::value_type, char>(), "");
    std::cout << a2.data() << std::endl;
 
    auto a3 = make_array("aa", "bb");
    static_assert(std::is_same<decltype(a3)::value_type, char const*>(),
        "fix your library");
    std::cout << a3[0] << std::endl;
 
    auto a4 = array_of<long>(2, 3U);
    static_assert(std::is_same<decltype(a4)::value_type, long>(), "");
    std::cout << a4[0] << std::endl;
 
    auto a5 = array_of<short>();
    static_assert(std::is_same<decltype(a5)::value_type, short>(), "");
    std::cout << a5.size() << std::endl;
 
    int a, b;
    auto a6 = array_of<std::reference_wrapper<int>>(a, b);
    std::cout << a6.size() << std::endl;
 
    // ** do not compile **
    //auto a4 = make_array(std::cref(""));
 
    // ** hard error **
    //char s[2][6] = { "nice", "thing" };
    //auto a3 = to_array(s);
}


Вопрос к знатокам: почему по опыту std::make_pair это сразу в стандарт не включили? Умышленно или просто забыли?

  Реализация
#include <array>
#include <functional>
 
template <typename... T>
using common_type_t = typename std::common_type<T...>::type;
 
template <typename T>
using remove_cv_t = typename std::remove_cv<T>::type;
 
template <bool, typename T, typename... U>
struct lazy_conditional_c;
 
template <typename T>
struct lazy_conditional_c<true, T>
{
    using type = typename T::type;
};
 
template <typename T, typename U>
struct lazy_conditional_c<true, T, U>
{
    using type = typename T::type;
};
 
template <typename T, typename U>
struct lazy_conditional_c<false, T, U>
{
    using type = typename U::type;
};
 
template <typename V, typename T, typename... U>
using If = lazy_conditional_c<V::value, T, U...>;
 
template <typename V, typename T, typename... U>
using If_t = typename If<V, T, U...>::type;
 
template <typename T>
struct identity_of
{
    using type = T;
};
 
template <template <typename> class F, typename... T>
struct no_type : std::true_type {};
 
template <template <typename> class F, typename T1, typename... T2>
struct no_type<F, T1, T2...> :
    std::integral_constant
    <
        bool,
        not F<T1>::value and no_type<F, T2...>::value
    >
{};
 
template <template <typename> class F, template <typename> class G>
struct composed
{
    template <typename T>
    using call = F<typename G<T>::type>;
};
 
template <typename T>
struct _is_reference_wrapper : std::false_type {};
 
template <typename T>
struct _is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
 
template <typename T>
using is_reference_wrapper =
    composed<_is_reference_wrapper, std::remove_cv>::call<T>;
 
template <typename... T>
constexpr auto make_array(T&&... t)
    -> std::array
    <
        If_t
        <
            no_type
            <
            composed
            <
                is_reference_wrapper,
                std::remove_reference
            >
            ::call,
            T...
            >,
            std::common_type<T...>
        >,
        sizeof...(T)
    >
{
    return {{ std::forward<T>(t)... }};
}
 
template <typename V, typename... T>
constexpr auto array_of(T&&... t)
    -> std::array
    <
        V,
        sizeof...(T)
    >
{
    return {{ std::forward<T>(t)... }};
}
 
template <size_t... I>
struct _indices {};
 
template <size_t N, size_t... I>
struct _build_indices : _build_indices<N - 1, N - 1, I...> {};
 
template <size_t... I>
struct _build_indices<0, I...> : _indices<I...> {};
 
template <typename T, size_t N, size_t... I>
constexpr auto _to_array(T (&arr)[N], _indices<I...>)
    -> std::array<remove_cv_t<T>, N>
{
    return {{ arr[I]... }};
}
 
template <typename T, size_t N>
constexpr auto to_array(T (&arr)[N])
    -> std::array<remove_cv_t<T>, N>
{
    return _to_array(arr, _build_indices<N>());
}
Re: make_array (a-la make_pair)
От: Кодт Россия  
Дата: 02.03.15 16:27
Оценка: +1
Здравствуйте, Skorodum, Вы писали:

S>Интересный код который позволяет создавать std::array в стиле std::make_pair и писать так:


S>    auto ch = 'a';
S>    auto const d = 65l;
S>    auto a1 = make_array(ch, d, 0);
S>    static_assert(std::is_same<decltype(a1)::value_type, long>(), "");


А почему это наименьший общий тип над char и int оказался long?

Тьфу! Там 65L, а не 651. Повбывавбы за такой стиль.
Перекуём баги на фичи!
Re[2]: make_array (a-la make_pair)
От: Skorodum Россия  
Дата: 02.03.15 21:30
Оценка:
Здравствуйте, Кодт, Вы писали:

А про возможность добавления в стандарт чего-то подобного что скажите? Мелочь, а приятно или есть какие-то подводные камни?
Re[3]: make_array (a-la make_pair)
От: watchmaker  
Дата: 02.03.15 22:08
Оценка:
Здравствуйте, Skorodum, Вы писали:

S>Здравствуйте, Кодт, Вы писали:


S>А про возможность добавления в стандарт чего-то подобного что скажите? Мелочь, а приятно или есть какие-то подводные камни?


Уже 7 лет обсуждают. И как раз из-за подводных камней никак процесс не сойдётся. То поддержка концептов для правильной реализации нужна, то принимается решение не ждать концептов, но всплывают другие проблемы.
Сейчас последняя версия предложения называется вроде как "make_array, revision 3" (rev 3 из-за того, что нумеровать не с начала стали). И судя по тому, что код с последнего предложения опять чуть поменялся, это всё ещё может быть не последняя версия. Но может к C++17 таки добьют — в планах LEWG вроде как есть.

С другой стороны такой низкий темп обсуждения, кажется, немало говорит об реальной востребованности и полезности подобной функциональности.
Отредактировано 02.03.2015 22:23 watchmaker . Предыдущая версия .
Re[4]: make_array (a-la make_pair)
От: Кодт Россия  
Дата: 02.03.15 23:45
Оценка:
Здравствуйте, watchmaker, Вы писали:

W>С другой стороны такой низкий темп обсуждения, кажется, немало говорит об реальной востребованности и полезности подобной функциональности.


Пропустил мимо внимания эти обсуждения.

У make_array с выводом типов, вообще, есть применения?

Лично меня как-то настораживают и напрягают попытки привести типы множества аргументов к какому-то наиболее общему.
Начать с того, что это ошибкоопасно. Например, множество указателей на классы из одной иерархии, и указатель на посторонний тип. Наиболее общим будет void*. Ну и получим кучу void*-ов.
Или char'ы и int — при том, что char в int можно привести двумя способами — как код символа (беззнаковый) и как попало (знаковый).

В случае с make_pair и make_tuple мы просто откладываем приведение типов аргументов на самый последний момент, — когда будем сопоставлять с tie, или поместим в контейнер известных типов кортежей, или же вообще какой-то из компонентов кортежа не потребуется.
С make_array такой номер не пройдёт, аргументы приводятся немедленно.

Где что-то такое похожее могло бы пригодиться — это обобщение range-based for
for(auto x : tuple_goes_here(a,b,c,d)) { ..... }
// развернуть в
{ auto x = a; ..... }
{ auto x = b; ..... }
{ auto x = c; ..... }
{ auto x = d; ..... }

На полиморфных лямбдах такую штуку сделать несложно.
Перекуём баги на фичи!
Re[5]: make_array (a-la make_pair)
От: watchmaker  
Дата: 03.03.15 00:28
Оценка:
Здравствуйте, Кодт, Вы писали:


К>У make_array с выводом типов, вообще, есть применения?

Так главное и единственное применение — запись сделать короче. Только сокращение числа нажатий на клавиши.

К>Лично меня как-то настораживают и напрягают попытки привести типы множества аргументов к какому-то наиболее общему.

К>Начать с того, что это ошибкоопасно.
Ну вот да. В наивной реализации make_array казалось бы можно просто возвращать массив из элементов типа std::common_type от аргументов. Но это даже для простой комбинации int и unsigned будет возвращать несколько неприятный результат. А есть ведь вещи и посложнее целочисленных типов. Так что с make_array главная сложность именно с определением какие преобразования разрешить и как при этом отлавливать их нарушения (а ведь это тоже нужно, если даже в варианте с явно заданным результирующим типом просто делать static_cast для аргументов, то всё равно останется место для ошибки).
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.