[英]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_shared
与std::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_type
是int
, second_type
是std::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.