繁体   English   中英

std :: make_shared与std :: initializer_list共享

[英]std::make_shared with std::initializer_list

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

它正常工作( 实时预览 ),但是当我尝试将std::make_sharedstd::initializer_list用作参数时,出现错误:

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>() } });

有可能使std::make_shared接受它吗?

提前致谢。

之所以

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> ,我们很高兴!

在上面,我指出了编译器通过寻找一个构造器来处理初始化器列表,该构造器直接接受初始化器列表或适当数量的参数,然后在必要时进行适当的隐式转换,然后将初始化器列表的元素传递给该参数。 例如,在上面的示例中,编译器可以发现需要将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>>> 此外,通过模板推导,不会将支撑初始列表推导出为std::initializer_list<T> ,因此即使您有类似

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

Derived具有采用std::initializer_list<int>的适当构造函数,出于相同的原因,它仍然不起作用: std::make_shared<Derived>不能为其参数推断任何类型。

如何解决这个问题? 不幸的是,我看不到任何简单的方法。 但是至少现在您应该知道为什么您写的内容行不通。

为此,您需要创建一个自定义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) );
}

或类似的东西。

另外,也可以将{...}直接“投射”到initializer_list<blah> (不是强制转换,而是构造)。

从理论上讲,足够的反射元编程支持将允许shared_ptr在没有traits类的情况下执行此操作,该类非常遥远。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM