6

In C++11, is it possible to do something similar to the following?

template<typename T, size_t N>
void foo(array<T, N> src) { ... }

...

foo({1, 2, 3})

I'm currently running GCC 4.8.

4 Answers 4

7

Yes, I managed to get the following work (since you allow something similar):

template<typename T, size_t N>
void foo(array<T, N> src) { ... }

...

foo('a', 'b');
foo(1, 2, 3);

Here is how:

#include <array>
#include <iostream>
#include <utility>
using namespace std;

template<typename T, unsigned long N>
void foo(array<T,N> src) { 

  for (auto e : src)
    cout << e << endl;
}

template<class T, class... Tail>
auto make_array(T head, Tail... tail) -> std::array<T, 1 + sizeof...(Tail)>
{
     std::array<T, 1 + sizeof...(Tail)> a = {{ head, tail ... }};
     return a;
}

template<class T, class... Tail> 
void foo(T&& head, Tail&&... values) {

    foo(make_array(std::forward<T>(head), std::forward<Tail>(values)...));
}

int main() {

  foo('a', 'b');

  foo(1, 2, 3);
}

I have tested this with gcc 4.7.2 and with clang 3.4 (trunk 184647), they work as expected.
Here is an online version at Stacked-Crooked. However, this code fails to compile at Ideone. Since I was unable to figure out the options passed to the compiler at Ideone, I've given up on that site.


I have shamelessly stolen the make_array function from @Pavel Minaev's answer to the How to emulate C array initialization “int arr[] = { e1, e2, e3, … }” behaviour with std::array? question. The other make_array suggestions caused compile errors that I couldn't fix.

This make_array function has limitations, please read the entire post; in particular the discussion std::array - if only it knew its size on comp.lang.c++.moderated is referenced. Apparently, getting a reasonable make_array is quite tricky. I wouldn't recommend the simple-minded make_array in this answer to be used in production code.


You wouldn't have any problems if the size was a template argument to std::initializer_list. Hence the question Why is the size not a template argument of std::initializer_list?

Sign up to request clarification or add additional context in comments.

2 Comments

Impressive... I think I need to read up some more to understand that make_array... :)
Why the downvote? What is wrong with the answer? The OP allows for something similar.
3

Apparently not. The standard (14.8.2.5) calls this an non-deduced context;

In certain contexts, however, the value does not participate in type deduction, but instead uses the values of template arguments that were either deduced elsewhere or explicitly specified.

...

The non-deduced contexts are:

...

  • A function parameter for which the associated argument is an initializer list (8.5.4) but the parameter does not have std::initializer_list or reference to possibly cv-qualified std::initializer_list type.

Example:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

EDIT: You can make the same thing work with std::vector, if you just use an initializer_list overload to make the deduction of the type work;

template<typename T>
  void foo(const std::vector<T>& src) { ...your code here... }
template<typename T>
  void foo(const std::initializer_list<T>& src) { foo(std::vector<T>(src)); }

foo({1,2,3});  // Compiles

...but sadly, since the size of initializer_list is not a template argument, I can't think of a way to make it deduce and forward the array size from the initializer_list in the same way as the type.

2 Comments

The vector solution seems to work without an initializer_list overload.
@jameszhao00 On which gcc? I only have 4.7.3 to test on currently. EDIT: Ah, 4.8, didn't see that you were the one asking the question :)
2

You could use an initializer list directly to achieve that syntax. e.g.:

#include <iostream>
#include <initializer_list>

void foo(std::initializer_list<int> il) {
  for (auto i: il)
    std::cout << i < std::endl;
}

int main() {
  foo({1,2,3});
}

or make it more generic:

template <typename T>
void foo(std::initializer_list<T> il) {
  ...

1 Comment

Going even further, why not template foo to any container?
1

It is possible with references to raw arrays:

template <typename T, size_t N>
void foo(T const (&x)[N]) {
    // x is [1, 2, 3], N = 3
}

int main() {
    foo({1, 2, 3});
    return 0;
}

Note that the array must be declared const.

5 Comments

This is nice. Do you have any insight why const allows this implicit conversion, and if so, please share it?
Is it because the reference must be const?
I don't know. I just read it in the "C++ Templates" book.
I believe I have the same book. Tons of stuff about array and pointer decay. Do you remember the section?
Sections 5.4 and 7.4 from the 2nd edition, I think

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.