initializer_list argument auto-expansion through variadic template

Viewed 47

In order to offer a nicer interface to my function f, I use a template :

#include <iostream>
#include <memory>
using namespace std;

using UPSTR = unique_ptr<char[]>;
void f(const initializer_list<UPSTR>& list){ 
  // process strings, concatenate, excluding certain patterns..
  for(auto& x:list) cout << x.get() << endl;
}

// I tried list expansion via variadic template :
template<typename... T>
void fnice(T&& ...args){
  f({ args... });
}

int main(){
  auto s = make_unique<char[]>(1);
  fnice(s,s);
}

So that a user would call fnice(upStr1, upStr2) as used to, instead of the less familiar f({ upStr1, upStr2 }).

What's the "deleted function" the compiler is complaining about ? How to fix it ?

Error :

in instantiation of 'UPSTR fnice(T&& ...) [with T = {std::unique_ptr<char [], std::default_delete<char []> >&, std::unique_ptr<char [], std::default_delete<char []> >&}; UPSTR = std::unique_ptr<char []>]':
18:8:   required from here

14:11: error: use of deleted function 'std::unique_ptr<_Tp [], _Dp>::unique_ptr(const std::unique_ptr<_Tp [], _Dp>&) [with _Tp = char; _Dp = std::default_delete<char []>]'
   14 |   return f({ args... });

Answer: @user17732522 (comment)

Can't even build an initializer_list from objects lacking move semantics ! (like unique_ptrs)

Thus, next best thing :

void f(const initializer_list<const char*>& list){
 for(auto &i : list) wcout << i<< endl; 
}

template<typename... T>
void fnice(T& ...args){
  std::initializer_list<const char*> t = { args.get()... };
  f(t);
}
1 Answers

As user17732522 pointed out the issue is with std::unique_ptr not being copyable and std::initializer_list not working with non-copyable types.

So you could either move the unique pointers into the initializer list, or you could use a different data type than the unique_ptr that do not transfer ownership, such as a raw or a shared pointer.

#include <iostream>
#include <memory>
using namespace std;

using UPSTR = unique_ptr<char[]>;
void f(const initializer_list<UPSTR>& list){ 
  // process strings, concatenate, excluding certain patterns..
  for(auto& x:list) cout << x.get() << endl;
}

// I tried list expansion via variadic template :
template<typename... T>
void fnice(T&& ...args){
  f({ std::move(args)... });
}

int main(){
  auto s = make_unique<char[]>(1);
  fnice(s,s);
}
Related