std :: make_unique(和emplace,emplace_back)对initializer_list参数的尴尬演绎

rub*_*nvb 8 c++ initializer-list unique-ptr c++14 c++17

说我有这个结构:

struct position
{
  int x, y;
};
Run Code Online (Sandbox Code Playgroud)

和另一个将其作为构造函数参数的类:

class positioned
{
public:
  positioned(position p) : pos(p) {}
private:
  position pos;
};
Run Code Online (Sandbox Code Playgroud)

我怎样才能搞定

auto bla = std::make_unique<positioned>({1,2});
Run Code Online (Sandbox Code Playgroud)

上班?

目前,编译器试图匹配initializer_list<int>并调用数组变量make_unique,这是愚蠢的,因为positioned只有一个构造函数.出现同样的问题emplaceemplace_back功能.几乎任何将其可变参数模板参数转发给类的构造函数的函数似乎都表现出这种行为.

我明白我可以解决这个问题

  1. 给出positioned一个两个int参数构造函数并将{}调用放入make_unique,或
  2. 显式指定参数的类型make_uniqueposition{1,2}.

两者看起来都过于冗长,因为在我看来(在make_unique实现中付出了一些努力),这可以在没有参数类型的过度规范的情况下得到解决.

这是一个可解决的make_unique实施缺陷,还是一个无法解决,无趣的边缘案例,没有人应该关心?

Nic*_*las 7

给定braced-init-list时,函数模板参数推导不起作用; 它只能根据实际表达式工作.

还应该注意的是positioned,{1, 2}无论如何都不能将列表初始化.这将尝试调用两个参数构造函数,并且positioned没有这样的构造函数.你需要使用positioned({1, 2})positioned{{1, 2}}.

因此,一般的解决方案是以make_unique某种方式神奇地重现它正在构造的类型的每个可能的构造函数的签名.这在C++中显然不是一件合理的事情.

另一种方法是使用lambda来创建对象,并编写一个替代make函数,使用C++ 17的保证省略规则将返回的prvalue应用于内部new表达式:

template<typename T, typename Func, typename ...Args>
std::unique_ptr<T> inject_unique(Func f, Args &&...args)
{
  return std::unique_ptr<T>(new auto(f(std::forward<Args>(args)...)));
}

auto ptr = inject_unique<positioned>([]() {return positioned({1, 2});});
Run Code Online (Sandbox Code Playgroud)

你甚至可以抛弃typename T参数:

template<typename Func, typename ...Args>
auto inject_unique(Func f, Args &&...args)
{
  using out_type = decltype(f(std::forward<Args>(args)...));
  return std::unique_ptr<out_type>(new auto(f(std::forward<Args>(args)...)));
}

auto ptr = inject_unique([]() {return positioned({1, 2});});
Run Code Online (Sandbox Code Playgroud)