Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

perfect forwarding, variadic template, initializer_list - together

Tags:

c++11

gcc 4.8.1 & clang 3.3, c++11 feature complete, I need to forward args in some function that constructs elements like make_shared/c++14 make_unique. But I have problem with type deduction/forwarding initializer lists/arrays

I need working example of foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) };, so std::initializer_list will deduce it arguments and function will forward them

#include <initializer_list>
#include <algorithm>

struct foo
  {
  struct pairs{ int a,b; };

  foo( int value ){}
  foo( std::initializer_list<pairs> elems, int value ){}
  };

//some custom allocation mechanizm
char store[sizeof(foo)*2];
char * store_ptr = &store[0];
void * allocfromstore( size_t sz ) { void * ret = store_ptr; store_ptr+= sz; return ret; }


template<typename ValueType, typename ArrType, typename... Args>
ValueType * make_it(std::initializer_list<ArrType> il, Args&&... args)
{
  ValueType * obj = new (allocfromstore(sizeof(ValueType))) ValueType( il, std::forward<Args>(args)...);
  return obj;
}


template<typename ValueType, typename... Args>
ValueType * make_it(Args&&... args)
{
  ValueType * obj = new (allocfromstore(sizeof(ValueType))) ValueType( std::forward<Args>(args)...);
  return obj;
}
std::initializer_list<foo::pairs> encapsulate( std::initializer_list<foo::pairs> il ){ return il; }
int main(){

  foo * foo0{ make_it<foo>( 10 ) };
  foo * foo1{ make_it<foo>( std::initializer_list<foo::pairs>({{1,1},{ 10,10 }}), 10 ) };
  foo * foo2{ make_it<foo>( encapsulate({{1,1},{ 10,10 }}), 10 ) };
  foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) };
return 0;
}

actually foo3 will fail with clang 3.3:

test.cpp:37:15: error: no matching function for call to 'make_it'
  foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) };
              ^~~~~~~~~~~~
test.cpp:18:13: note: candidate template ignored: couldn't infer template argument 'ArrType'
ValueType * make_it(std::initializer_list<ArrType> il, Args&&... args)
            ^
test.cpp:26:13: note: candidate function not viable: requires 0 arguments, but 2 were     provided
ValueType * make_it(Args&&... args)
            ^
1 error generated.

and with gcc 4.8.1:

test.cpp: In function ‘int main()’:
test.cpp:37:51: error: no matching function for call to ‘make_it(<brace-enclosed initializer list>, int)’
   foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) };
                                                   ^
test.cpp:37:51: note: candidates are:
test.cpp:18:13: note: template<class ValueType, class ArrType, class ... Args>  ValueType* make_it(std::initializer_list<ArrType>, Args&& ...)
 ValueType * make_it(std::initializer_list<ArrType> il, Args&&... args)
             ^
test.cpp:18:13: note:   template argument deduction/substitution failed:
test.cpp:37:51: note:   couldn't deduce template parameter ‘ArrType’
   foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) };
                                                   ^
test.cpp:26:13: note: ValueType* make_it(Args&& ...) [with ValueType = foo; Args = {}]
     ValueType * make_it(Args&&... args)
                 ^
test.cpp:26:13: note:   candidate expects 0 arguments, 2 provided
like image 903
Artur Bac Avatar asked Aug 01 '13 17:08

Artur Bac


1 Answers

Uniform initialization does not work with forwarding. You can't forward initialization parameters (directly) and then use those to initialize some object. You can initialize a parameter of a function and then forward that along to someone else, but you can't forward initialization parameters.

Braced-init-lists (aka: the {} stuff) do not participate in template argument deduction. That's why your foo3 gets an error when you do it. The compiler can't know what type you want it converted into, so it fails.

Even if you tried to store {{1, 1}, {10, 10}} in an auto variable, you wouldn't get what you want. auto can type-deduce braced-init-lists into initializer_list types, but you'd get something like initializer_list<initializer_list<int>>.

The compiler simply is not going to go through your template code and figure out what type you really meant.

like image 181
Nicol Bolas Avatar answered Sep 20 '22 12:09

Nicol Bolas