Re: std::array - if only it knew its size
On 2011-04-06 21:38, Daniel Kr?gler wrote:
Since the original proposal done
in LWG 851 I had developed make_array a bit further and here is my
currently best approximation: The advantages are:
1) make_array can now be used in constant expressions
2) Users can provide there own destination type
3) Support for empty arrays
4) Validation that all arguments are implicitly convertible to the
destination type (Remove this rule, if you don't like it, or replace it
by a corresponding is_constructible test or sfinae the template away).
[..]
As pointed out by Johannes Schaub in another thread, I used a non-portable initialization syntax for the std::array in my previous message. And as pointed out by myself in a reply to his message there is the second problem that there does not exist a portable direct-list-initialization syntax for zero-length arrays (which should be fixed by a library issue). The following implementation tries to solve this issues via a helper factory, fortunately not loosing the above features:
#include <type_traits>
#include <utility>
#include <array>
// Just imagine that std::forward were required to be constexpr...
template <class T>
constexpr T&&
cforward(typename std::remove_reference<T>::type& t)
{
return static_cast<T&&>(t);
}
template <class T>
constexpr T&&
cforward(typename std::remove_reference<T>::type&& t)
{
static_assert(!std::is_lvalue_reference<T>::value,
"T must not be an lvalue-reference"
);
return static_cast<T&&>(t);
}
template<class T, class... U>
struct all_convertible_to;
template<class T>
struct all_convertible_to<T> : std::true_type {};
template<class T, class U>
struct all_convertible_to<T, U> : std::is_convertible<U, T>::type {};
template<class T, class U, class... R>
struct all_convertible_to<T, U, R...> : std::conditional<
std::is_convertible<U, T>::value, all_convertible_to<T, R...>,
std::false_type>::type::type
{
};
// This tag type is used to detect whether we perform automatic
// type deduction, else the type is user-provided:
struct auto_element_type_t;
template<class T, class...>
struct deduce_element_type
{
typedef T type;
};
template<class... T>
struct deduce_element_type<auto_element_type_t, T...>
{
typedef typename std::decay<typename std::common_type<T...>::type>::type type;
};
// Sfinae protection for the empty sequence case: User code
// is required to provide the destination type in this case:
template<>
struct deduce_element_type<auto_element_type_t>
{
};
template<class T, class... U>
struct array_creator
{
static constexpr std::array<T, sizeof...(U)> create(U&&... u)
{
return std::array<T, sizeof...(U)>{ {static_cast<T>(cforward<U>(u))...} };
}
};
template<class T>
struct array_creator<T>
{
static constexpr std::array<T, 0> create()
{
return std::array<T, 0>();
}
};
template <class D = auto_element_type_t, class... T>
constexpr auto make_array(T&&... t) ->
std::array<typename deduce_element_type<D, T...>::type, sizeof...(T)>
{
typedef typename deduce_element_type<D, T...>::type U;
static_assert(all_convertible_to<U, T...>::value,
"All argument types must be implicitly convertible to the element type of std::array"
);
return array_creator<U, T...>::create(cforward<T>(t)...);
}
int main()
{
auto a = make_array(1, 2);
auto b = make_array(5, 1.2);
auto c = make_array<float>(-12, 7.3);
auto d = make_array<bool>();
constexpr auto ca = make_array(-1, +1, -2, +2);
constexpr auto cb = make_array<bool>();
constexpr auto i1 = 2, i2 = 3;
constexpr auto cc = make_array(i1, i2);
}
Enjoy & Greetings from Bremen,
Daniel Kr?gler
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]