std :: make_shared与std :: initializer_list

时间:2014-06-15 21:59:29

标签: c++ c++11 std

#include <iostream>
#include <memory>

class Base
{
public:
    Base() {}
};

class Derived : public Base
{
public:
    Derived() {}
    Derived(std::initializer_list<std::pair<int, std::shared_ptr<Base>>>) {}
};

int main(int argc, char ** argv)
{
    auto example = new Derived({
        { 0, std::make_shared<Derived>() }
    });

    return 0;
}

它正常工作(live preview),但当我尝试使用std::make_shared作为参数时,我遇到了错误:

std::initializer_list

正如您在live preview上看到的那样。

  

错误:功能参数太多......

仅在我执行此操作时才有效(live preview):

auto example = new Derived({
    { 0, std::make_shared<Derived>({
        { 0, std::make_shared<Derived>() }
    }) }
});

我想知道的是:为什么只有当我在auto example = new Derived({ { 0, std::make_shared<Derived>(std::initializer_list<std::pair<int, std::shared_ptr<Base>>> { { 0, std::make_shared<Derived>() } }) } }); 上传递std::initializer_list作为参数而不是像这样使用std::make_shared时,它才有效:

{{}}

是否可以让auto example = new Derived({ { 0, std::make_shared<Base>() } }); 接受它?

提前致谢。

2 个答案:

答案 0 :(得分:16)

原因

auto example = new Derived({
    { 0, std::make_shared<Derived>() }
});

的工作原理是编译器知道它必须匹配初始化器

{{ 0, std::make_shared<Derived>() }}

以某种方式使用构造函数

Derived::Derived(std::initializer_list<std::pair<int, std::shared_ptr<Base>>>) {}

因此很清楚初始化列表的元素

{ 0, std::make_shared<Derived>() }

需要用于初始化std::pair<int, std::shared_ptr<Base>>。然后它找到一对带有两个元素的构造函数,

pair::pair (const first_type& a, const second_type& b);

其中first_typeintsecond_typestd::shared_ptr<Base>。最后我们看到参数std::make_shared<Derived>()被隐式转换为std::shared_ptr<Base>,我们很高兴去![/ p>

在上面,我指出编译器通过查找直接接受初始化列表的构造函数或适当数量的参数来处理初始化列表,然后在适当的隐式转换后将初始化列表的元素传递给它们。如有必要。例如,编译器可以确定您的std::shared_ptr<Derived>需要在上面的示例中隐式转换为std::shared_ptr<Base>,因为该对的构造函数需要它。

现在考虑

std::make_shared<Derived>({
        { 0, std::make_shared<Derived>() }
    })

问题是make_shared<Derived>是一个部分专用的函数模板,可以接受任意数和类型的参数。因此,编译器不知道如何处理初始化列表

{{ 0, std::make_shared<Derived>() }}

在重载解析时,它不知道需要将其转换为std::initializer_list<std::pair<int, std::shared_ptr<Base>>>。此外, braced-init-list 永远不会通过模板推导推断为std::initializer_list<T>,所以即使你有像

这样的东西
std::make_shared<Derived>({0, 0})

Derived有一个适当的构造函数,std::initializer_list<int>,它仍然无法工作,原因相同:std::make_shared<Derived>无法推断出其参数的任何类型。< / p>

如何解决这个问题?不幸的是,我看不到任何简单的方法。但至少现在你应该知道为什么你写的东西不起作用。

答案 1 :(得分:5)

为此,您需要创建自定义make_shared_from_list,因为make_shared不支持非显式初始化列表。其原因很好地由@brian描述。

我会使用traits类将类型T映射到初始化列表的类型。

template<class>struct list_init{};// sfinae support
template<> struct list_init<Derived>{using type=std::pair<int, std::shared_ptr<Base>>;};

template<class T>using list_init_t=typename list_init<T>::type;

template<class T>
std::shared_ptr<T> make_shared_from_list( std::initializer_list<list_init_t<T>> list ){
  return std::make_shared<T>( std::move(list) );
}

或类似的东西。

或者,&#34;演员&#34;直接{...}initializer_list<blah>(不是演员,而是建筑)可能有效。

从理论上讲,足够的反射元编程支持将允许shared_ptr在没有特征类的情况下执行此操作,该类位于管道的相当远的位置。