| #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_else = lazy_conditional_c<V::value, T, U...>; | |
| template <typename V, typename T, typename... U> | |
| using If = typename if_else<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 V = void, typename... T> | |
| constexpr auto make_array(T&&... t) | |
| -> std::array | |
| < | |
| If | |
| < | |
| std::is_void<V>, | |
| std::common_type<T...>, | |
| identity_of<V> | |
| >, | |
| sizeof...(T) | |
| > | |
| { | |
| static_assert(not std::is_void<V>() or | |
| no_type | |
| < | |
| composed | |
| < | |
| is_reference_wrapper, | |
| std::decay | |
| > | |
| ::call, | |
| T... | |
| >(), "T shall not be reference_wrapper"); | |
| 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, size_t... I> | |
| constexpr auto _to_array(T (&&arr)[N], _indices<I...>) | |
| -> std::array<remove_cv_t<T>, N> | |
| { | |
| return {{ std::move(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>()); | |
| } | |
| template <typename T, size_t N> | |
| constexpr auto to_array(T (&&arr)[N]) | |
| -> std::array<remove_cv_t<T>, N> | |
| { | |
| return _to_array(std::move(arr), _build_indices<N>()); | |
| } | |
| #include <iostream> | |
| #include <memory> | |
| 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 = make_array<long>(2, 3U); | |
| static_assert(std::is_same<decltype(a4)::value_type, long>(), ""); | |
| std::cout << a4[0] << std::endl; | |
| auto a5 = make_array<short>(); | |
| static_assert(std::is_same<decltype(a5)::value_type, short>(), ""); | |
| std::cout << a5.size() << std::endl; | |
| int a, b; | |
| auto a6 = make_array<std::reference_wrapper<int>>(std::ref(a), b); | |
| std::cout << a6.size() << std::endl; | |
| auto a7 = to_array<std::pair<int, float>>( | |
| { { 3, .0f }, { 4, .1f }, { 4, .1e23f } }); | |
| static_assert( | |
| std::is_same<decltype(a7)::value_type, std::pair<int, float>>(), | |
| ""); | |
| auto a8 = to_array({ std::make_unique<int>(3) }); | |
| std::cout << *a8[0] << std::endl; | |
| auto a9 = to_array({ 0, 2, 1, 3 }); | |
| std::cout << a9.size() << std::endl; | |
| // ** do not compile ** | |
| //auto a7 = make_array(std::cref("")); | |
| // ** hard error ** | |
| //char s[2][6] = { "nice", "thing" }; | |
| //auto a8 = to_array(s); | |
| } |
In the stackoverflow answer, all elements are required to be implicitly convertible to the first element:
make_array(1, 1L) -> std::array<int, 2>
In this gist, common type is calculated:
make_array(1, 1L) -> std::array<long, 2>
Alternatively, you can specify the target type:
make_array<long long>(1, 1L) -> std::array<long long, 2>
Although such a syntax works in the stackoverflow version, the first argument will no longer use perfect forwarding, because it abandons the deduction behavior.
What is the license for this gist ?
I need this very badly in the standard! This is an extremely useful function to me! Thanks for this gist and the proposal!
I actually get there from a link in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4391.html
so it looks like it will be in a TS or C++17
I can't get this to work with VS 2015, curious if anyone else has had luck?
struct Point { int x, y; };
auto triangle = std::make_array<Point>({3, 3}, {5, 5}, {1, 5});
c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1292): error C2440: 'return': cannot convert from 'Point [3]' to 'Point (&&)[3]'
c:\program files (x86)\microsoft visual studio 14.0\vc\include\type_traits(1292): note: You cannot bind an lvalue to an rvalue reference
make_array.cc(125): note: see reference to function template instantiation 'Point (&&std::move<T(&)[3]>(_Ty) noexcept)[3]' being compiled
It works fine with GCC 6.
I love this, but am wondering if it can be improved. Would the syntax in #1 and #2 be possible?
// #1 How I would expect std::array to work
std::array<VkDescriptorPoolSize> poolSizes = (
VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
, VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);
// #2 How I would expect make_array to work
auto poolSizes = make_array<VkDescriptorPoolSize>(
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);
// #3 How make_array actually works
auto poolSizes = make_array(
VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount }
, VkDescriptorPoolSize{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 }
);
// #4 How C/C++ works without std::array
VkDescriptorPoolSize poolSizes[] = {
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, descriptorCount}
, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1}
};
edit: I realized #2 is -supposed- to work, but it does not for me (VS2017)
I haven't studied the code yet but what does all this do differently than http://stackoverflow.com/a/6114299/701092?