简体   繁体   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;
}

It works ( live preview ) normally, but when I try to use std::make_shared with the std::initializer_list as argument I got errors: 它正常工作( 实时预览 ),但是当我尝试将std::make_sharedstd::initializer_list用作参数时,出现错误:

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

As you can see here on the live preview . 如您在此处看到的实时预览

error: too many arguments to function... 错误:功能太多参数...

It works only when I do this ( live preview ): 它仅在执行此操作( 实时预览 )时有效:

auto example = new Derived({
    { 0, std::make_shared<Derived>(std::initializer_list<std::pair<int, std::shared_ptr<Base>>> {
        { 0, std::make_shared<Derived>() }
    }) }
});

What I want to know is: Why it works only when I pass the std::initializer_list as argument on std::make_shared instead of using {{}} just like this: 我想知道的是:为什么仅当我将std::initializer_list作为参数传递给std::make_shared而不是像这样使用{{}}时,它才起作用:

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

Is that possible to make std::make_shared accept it? 有可能使std::make_shared接受它吗?

Thanks in advance. 提前致谢。

The reason why 之所以

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

works is that the compiler knows that it has to match the initializer 起作用的是编译器知道它必须匹配初始化程序

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

somehow with the constructor 以某种方式与构造函数

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

So it is clear that the element of the initializer list, 因此很明显,初始化器列表的元素,

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

needs to be used to initialize a std::pair<int, std::shared_ptr<Base>> . 需要用于初始化std::pair<int, std::shared_ptr<Base>> It then finds a constructor for the pair that takes two elements, 然后,为包含两个元素的一对查找构造函数,

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

where first_type is int and second_type is std::shared_ptr<Base> . 其中first_typeintsecond_typestd::shared_ptr<Base> So finally we see that the argument std::make_shared<Derived>() is implicitly converted to std::shared_ptr<Base> , and we're good to go! 所以最后我们看到参数std::make_shared<Derived>()被隐式转换为std::shared_ptr<Base> ,我们很高兴!

In the above, I pointed out that the compiler handles initializer lists by looking for a constructor that accepts either an initializer list directly, or the appropriate number of arguments, to which the initializer list's elements are then passed, after appropriate implicit conversions if necessary. 在上面,我指出了编译器通过寻找一个构造器来处理初始化器列表,该构造器直接接受初始化器列表或适当数量的参数,然后在必要时进行适当的隐式转换,然后将初始化器列表的元素传递给该参数。 For example, the compiler can figure out that your std::shared_ptr<Derived> needs to be implicitly converted to std::shared_ptr<Base> in the above example only because the pair's constructor demands it. 例如,在上面的示例中,编译器可以发现需要将std::shared_ptr<Derived>隐式转换为std::shared_ptr<Base> ,这是因为该对的构造函数需要这样做。

Now consider 现在考虑

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

The problem is that make_shared<Derived> is a partially specialized function template that can accept arguments of arbitrary number and type. 问题在于make_shared<Derived>是部分专用的函数模板,可以接受任意数量和类型的参数。 Because of this, the compiler has no idea how to handle the initializer list 因此,编译器不知道如何处理初始化程序列表

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

It doesn't know at the time of overload resolution that it needs to be converted to std::initializer_list<std::pair<int, std::shared_ptr<Base>>> . 在重载解决方案时尚不知道需要将其转换为std::initializer_list<std::pair<int, std::shared_ptr<Base>>> Additionally, a braced-init-list is never deduced as std::initializer_list<T> by template deduction, so even if you had something like 此外,通过模板推导,不会将支撑初始列表推导出为std::initializer_list<T> ,因此即使您有类似

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

and Derived had an appropriate constructor taking std::initializer_list<int> , it still wouldn't work, for the same reason: std::make_shared<Derived> would not be able to deduce any type for its argument. Derived具有采用std::initializer_list<int>的适当构造函数,出于相同的原因,它仍然不起作用: std::make_shared<Derived>不能为其参数推断任何类型。

How to fix this? 如何解决这个问题? Unfortunately, I cannot see any easy way. 不幸的是,我看不到任何简单的方法。 But at least now you should know why what you wrote doesn't work. 但是至少现在您应该知道为什么您写的内容行不通。

For this to work, you need to create a custom make_shared_from_list , as make_shared does not support non-explicit initializer lists. 为此,您需要创建一个自定义make_shared_from_list ,因为make_shared不支持非显式的初始化列表。 The reason is described well by @brian. 原因由@brian很好地描述了。

I would use a traits class to map a type T to the type of initializer list. 我将使用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) );
}

or something like that. 或类似的东西。

Alternatively, "cast" the {...} to the initializer_list<blah> directly (not a cast, but rather a construction) may work. 另外,也可以将{...}直接“投射”到initializer_list<blah> (不是强制转换,而是构造)。

In theory, sufficient reflection metaprogramming support would allow shared_ptr to do this without the traits class, bit that is pretty far down the pipe. 从理论上讲,足够的反射元编程支持将允许shared_ptr在没有traits类的情况下执行此操作,该类非常遥远。

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

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