0

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);
}
ExpertNoob1
  • 900
  • 1
  • 8
  • 17
  • Please **[edit]** your question with an [mre] or [SSCCE (Short, Self Contained, Correct Example)](http://sscce.org) – NathanOliver Sep 09 '22 at 14:07
  • I think my question is perfectly clear, adding main() and cout<< would make for longer reading without added value (*to those who would actually be able to answer*). – ExpertNoob1 Sep 09 '22 at 14:13
  • We dont really need that, but the error message will tell you and us what the problem is. Since you do not have enough code here for us to compile it and see what it says, there isn't a lot we can help with. There can be many reason why the compiler is complaining about you using a deleted function and the error message will tell you what that reason is. – NathanOliver Sep 09 '22 at 14:16
  • Aside from the there seemingly being a typo (`UPSTR` vs `UPWSTR`), your first function will already not be usable. It has nothing to do with the variadic template. `f({ upStr1, upStr2 })` also fails. `std::initializer_list` doesn't work with non-copyable types at all. (That's basically a design defect.) Use a reference to a built-in array of `UPWSTR` instead or directly use a variadic template. – user17732522 Sep 09 '22 at 14:28
  • related/dupe: https://stackoverflow.com/questions/8193102/initializer-list-and-move-semantics, https://stackoverflow.com/questions/9618268/initializing-container-of-unique-ptrs-from-initializer-list-fails-with-gcc-4-7 – NathanOliver Sep 09 '22 at 14:31
  • Also, a `std::unique_ptr` must be `std::move`d into a function if it is supposed to take ownership. It can not be copied. If you don't want the function to take ownership pass raw pointers (form `.get()`) instead. – user17732522 Sep 09 '22 at 14:34
  • @NathanOliver: added a complete example. – ExpertNoob1 Sep 09 '22 at 14:59
  • @user17732522: right, even `f` can't be called with an `initializer_list`. From the post you point to, it looks like `initializer_list` cannot be used for this. Any solutions to keep the interface "nice", meaning the user passes a list of UPSTRs, without packing them first in a vector/array/.. ? (or in a simple fashion, like: `{a,b,..}`, the main advantage of `initializer_list` in this case). – ExpertNoob1 Sep 09 '22 at 15:05
  • Instead of having `f` do the work on all of the strings, if you can just have it do the work for a single string. Then you can change `fnice` to `template void fnice(T&& ...args) { (f(args), ...); }` which will call `f` on each parameter of `fnice` – NathanOliver Sep 09 '22 at 15:10
  • I know and use that comma operator. My actual `f` has to return a string (a form of conditional concatenation). – ExpertNoob1 Sep 09 '22 at 16:11

1 Answers1

0

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);
}
joergbrech
  • 2,056
  • 1
  • 5
  • 17
  • Thank you, but this code loses the the arguments to the calling context : after call to `fnice(s)`, `s` is no longer valid. Not the intended use. – ExpertNoob1 Sep 09 '22 at 16:27