1

I have a function template that takes an arbitrarily nested list and returns an array:

#include <array>
#include <initializer_list>

template<size_t N, typename List>
std::array<size_t,N> some_function (const List& list)
{
    // N is the number of times the list is nested.
    std::array<size_t,N> arr;
    return arr;
}

When I use this function for some nested std::initializer_list, like this:

int main () {
    using List = std::initializer_list<std::initializer_list<double>>;
    List list = {{1.,2.,3.},{4.,5.,6.}};

    std::array<size_t,2> arr;
    arr = some_function (list);
    return 0;
}

I receive the error that type N can not be deduced

couldn't deduce template parameter ‘N’

Question

  • How can I improve my function template to deduce the number of times a list is nested?
  • Are there better alternatives than std::initializer_list for this case?
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Ali
  • 1,023
  • 1
  • 9
  • 31
  • What do you expect `arr` to be after `arr = some_function (list);`. `N` in `std::array` is the size of the array. – super Mar 09 '20 at 11:28
  • Also, you are using a `std::array` with the value type `std::size_t`, but your list holds doubles. Please clarify your question. – super Mar 09 '20 at 11:29
  • As far as I understand the assignment `arr = some_function (list)` isn't used in type deduction. So only `some_function (list)` is used. But then there isn't a way for compiler to know the size of array. – miszcz2137 Mar 09 '20 at 11:29
  • The size of an initializer_list is not known at compile time. See: https://stackoverflow.com/questions/41311023/get-the-size-of-stdinitializer-list-at-compile-time – John Zwinck Mar 09 '20 at 11:30
  • are you trying to get `{ 2, 3 }` from `{{1.,2.,3.},{4.,5.,6.}}`? I.e. the size of each dimension? – Caleth Mar 09 '20 at 11:39
  • @super Thanks. Just to clarify the type of 'std::array' has nothing to do with type of the input List. – Ali Mar 09 '20 at 12:05
  • @JohnZwinck Thanks. This makes sense. – Ali Mar 09 '20 at 12:07
  • @Caleth Not exactly this but something like that. I simplified the function, so it may look strange. – Ali Mar 09 '20 at 12:08

1 Answers1

4

You can write two overloaded constexpr function templates to calculate the nested times, with the help of std::enable_if and SFINAE.

// types have not member type value_type
template <typename T, typename = void>
struct has_value_type: std::false_type {};
// types have member type value_type
template <typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};

// return nested times as 0 for types without member type value_type
template<typename T>
constexpr std::enable_if_t<!has_value_type<T>::value, size_t> get_nested_times() {
    return 0;
}
// return nested times as 1 plus times got on the nested type recursively
template<typename T>
constexpr std::enable_if_t<has_value_type<T>::value, size_t> get_nested_times() {
    return 1 + get_nested_times<typename T::value_type>();
}

then you can get the nested times at compile-time as

template<typename List>
auto some_function (const List& list)
{
    // N is the number of times the list is nested.
    constexpr auto N = get_nested_times<List>();
    std::array<size_t, N> arr;
    return arr;
}

LIVE

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Thanks, this is a very nice solution. However, I think it is still necessary to call the function explicitly as `some_function(),List> ` , because the type deduction will fail otherwise. – Ali Mar 09 '20 at 12:18
  • 1
    @Ali If you can change the function signature as I showed then you don't have to do that. You can get `N` inside the `some_function` instead of specifying it as template parameter. – songyuanyao Mar 09 '20 at 12:22
  • Notice that your example will work with C++17 as well as the C++20 you compiled it with in the live demo. – Spencer Mar 09 '20 at 12:24
  • @songyuanyao Yes, you are right. I forgot to update the definition of function template. – Ali Mar 09 '20 at 12:26