I would like a type trait to get the element type of either a std::array
or a plain old C-style array, eg. it should return char
when provided with either std::array<char, 3>
or char[3]
.
The mechanisms to do this appear to be only partially in place... I can use ::value_type
on the std::array
, and std::remove_all_extents
on the plain array, but I can't find a single type trait that combines both and I'm unable to write one myself.
I've got as far as this:
#include <array>
#include <type_traits>
template <class T>
using element_type = typename std::conditional<
std::is_array<T>::value,
typename std::remove_all_extents<T>::type,
typename T::value_type
>::type;
It works just fine for std::array
, of course:
int main()
{
static_assert(
std::is_same<char, element_type<std::array<char, 3>>>::value,
"element_type failed");
}
but breaks when I pass it a plain array, because obviously plain arrays don't have a ::value_type
.
static_assert(std::is_same<char, element_type<char[3]>>::value, "element_type failed");
just gives errors like "'T': must be a class or namespace when followed by '::'", as you'd expect.
If I were writing a function, I'd use std::enable_if
to hide the offending template instantiation, but I don't see how this approach can be used in a type trait.
What is the correct way to solve this problem?
A C-Style array is just a "naked" array - that is, an array that's not wrapped in a class, like this: char[] array = {'a', 'b', 'c', '\0'}; Or a pointer if you use it as an array: Thing* t = new Thing[size]; t[someindex]. dosomething();
So - yes, a pointer can very well be used with STL algorithms.
c-style array as method parameter with size information Of course, the template concept can also be used for a c-style array. Instead of passing a pointer to the first element, you can pass the array as a reference to template function. This will allow to pass the array with its specific size.
C++ Type Traits Standard type traits The type_traits header contains a set of template classes and helpers to transform and check properties of types at compile-time. These traits are typically used in templates to check for user errors, support generic programming, and allow for optimizations.
For a very generic solution which supports any type of container/array supported by std::begin
:
template<typename T>
using element_type_t = std::remove_reference_t<decltype(*std::begin(std::declval<T&>()))>;
std::begin
as you may know returns an iterator. Dereferencing it gives you the value of it, which you can get the type of using decltype
. The std::remove_reference_t
is necessary because iterators return references to the element they are pointing at. As a result, this works for every single type for which std::begin
has an overload.
What are the functions/operators you can call on both std::array
and C-style arrays? operator[]
of course:
template <class Array>
using array_value_type = decay_t<decltype(std::declval<Array&>()[0])>;
This will work for anything that supports looking up by integer, including std::vector
, std::map<std::size_t, T>
, etc.
If you want to distinguish between what you get from a const array vs a non-cost array you might want to create 2 type traits named something along the lines of:
template <class Array>
using array_element_t = decay_t<decltype(std::declval<Array>()[0])>;
template <class Array>
using array_value_t = remove_reference_t<decltype(std::declval<Array>()[0])>;
The second trait here preserves the constness of the Array
type passed in while the first one strips it. There are certainly use cases for both of these.
A way to do this is to dispatch to specialized templates
template<typename>
struct arr_trait;
template<typename T, size_t N>
struct arr_trait<T[N]> {using type = T;};
template<typename T, size_t N>
struct arr_trait<std::array<T, N>> {using type = T;};
template<typename T>
struct arr_trait<T&> : arr_trait<T> {};
template<typename T>
struct arr_trait<T&&> : arr_trait<T> {};
template<typename T>
using element_type = typename arr_trait<T>::type;
Live
The reason std::conditional
is failing is because it doesn't support (and neither can it as far as I know) support short-circuiting, and both types will be evaluated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With