【问题标题】:perfect forwarding, variadic template, initializer_list - together完美转发、可变参数模板、initializer_list - 一起
【发布时间】:2013-08-01 17:11:53
【问题描述】:

gcc 4.8.1 & clang 3.3,c++11 功能完成, 我需要在一些构造像make_shared/c++14 make_unique这样的元素的函数中转发args。但我对类型推导/转发初始化列表/数组有问题

我需要foo * foo3{ make_it<foo>( {{1,1},{ 10,10 }}, 10 ) }; 的工作示例,所以std::initializer_list 会推断出它的参数并且函数会转发它们

#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;
}

实际上 foo3 会在 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.

并使用 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

【问题讨论】:

    标签: c++11


    【解决方案1】:

    统一初始化不适用于转发。您不能(直接)转发初始化参数,然后使用这些参数来初始化某些对象。您可以初始化函数的参数,然后将其转发给其他人,但您不能转发初始化参数。

    支撑初始化列表(又名:{} 东西)不参与模板参数推导。这就是为什么您的foo3 在执行此操作时会出错。编译器不知道你想把它转换成什么类型​​,所以它失败了。

    即使您尝试将{{1, 1}, {10, 10}} 存储在auto 变量中,您也不会得到您想要的。 auto 可以将花括号初始化列表类型推断为 initializer_list 类型,但您会得到类似 initializer_list&lt;initializer_list&lt;int&gt;&gt; 的内容。

    编译器根本不会检查您的模板代码并找出您真正的意思。

    【讨论】:

    • 但我想知道为什么它不能与未命名的 struct 一起工作,就像构造函数一样工作。初始化器列表
    • @ArturBac 编译器无法判断{{0,0},{1,1}}std::initializer_list&lt;std::pair&lt;int,int&gt;&gt;,还是std::initializer_list&lt;std::complex&lt;int&gt;&gt;,还是std::intializer_list&lt;foo::pairs&gt;,...
    • 是的,我知道它不能确定它是struct,所以它是完美转发逻辑中的一个漏洞,我是为构造函数转发参数而设计的,在某些情况下它不能使用。如果我们告诉编译器名为 struct 的编译器(只命名列表中的第一个结构就足够了): foo * foo3{ make_it( {foo::pairs{1,1},{ 10,10 }}, 10) };
    • @ArturBac: "所以它是完美转发逻辑的一个漏洞" 完美转发是指转发对象。一个花括号初始化列表不是一个对象;它是 for 对象的初始化器,但在构造某些东西之前它不是对象。 “只命名列表中的第一个结构就足够了”这假设支撑初始化列表只能用于列表的事物。它们的用途远不止于此。
    【解决方案2】:

    是否可以接受仅适用于foo::pairs 列表的解决方案?

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

    【讨论】:

    • 谢谢,这个我知道,我想知道参数 std::initializer 列表的推导是否可以使用未命名的结构作为参数。
    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 2013-01-06
    • 1970-01-01
    • 2017-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-04
    相关资源
    最近更新 更多