`std::make_shared` 因空参数包而失败

`std::make_shared` fails for empty parameter pack

背景

我有一个可变 class 模板。

template<typename... Ts>
class Foo {
public:
    Foo(Ts... values) {
        
    }    
};

使用这个 class,我可以创建对象 with/without 参数。

Foo<int> f1{1};
Foo<int, double> f2{1, 2.0};
Foo f3{};

我什至可以创建一个共享指针。

auto f4 = std::make_shared<Foo<int, double>>(1, 2.0);

问题

当我想创建一个没有参数的共享指针时,问题就出现了。这些方法都不起作用:

auto f5 = std::make_shared<Foo>();
Foo f5{}
auto f5_sharedPtr = std::make_shared(std::move(f5));
main.cpp:27:37: error: no matching function for call to ‘make_shared class Foo>()’
     auto f5 = std::make_shared<Foo>();
                                     ^
In file included from /usr/include/c++/7/memory:81:0,
                 from main.cpp:9:
/usr/include/c++/7/bits/shared_ptr.h:703:5: note: candidate: template std::shared_ptr<_Tp> std::make_shared(_Args&& ...)
     make_shared(_Args&&... __args)
     ^~~~~~~~~~~
/usr/include/c++/7/bits/shared_ptr.h:703:5: note:   template argument deduction/substitution failed:

问题

  1. 有人能解释一下这是怎么回事吗?
  2. 有谁知道如何进行这项工作?

正确的语法是std::make_shared<Foo<>>()

Foo<int,double> 是一种类型,这就是 std::make_shared<Foo<int,double>> 起作用的原因。

问题是 Foo,没有 <> 是模板,不是类型,会导致编译错误。

编辑:

密码

Foo f3{};

是一种特殊情况。在这种情况下,您正在使用 C++17 功能 Class Template Argument Deduction。这将 f3 的类型推断为 Foo<> 这也是让你写

的原因
Foo f4{1, 2.0}

这会将 f4 的类型推断为 Foo<int, double>

cppreference 页面提供了有关何时可以使用此功能的更多示例

作为对@Dave S 回答的补充,我想补充一点,可以编写一个按您预期工作的 std::make_shared() 版本。

诀窍是让shared_ptr<>的“type”参数成为类型模板,在函数内部实现类型。

#include <memory>
#include <type_traits>


template<template<typename...> typename T, typename... ArgsT>
auto my_make_shared(ArgsT&&... args) {
    // Create a type from T template according to what's in the arguments
    using ResT = T<std::decay_t<ArgsT>...>;

    // Now that we have a type instead of a template, we can call std::make_shared()
    return std::make_shared<ResT>(std::forward<ArgsT>(args)...);
}

template<typename... Ts>
class Foo {
public:
    Foo(Ts... values) {}    
};

void foo() {
    auto tmp = my_make_shared<Foo>(1, 2.0, "aaa");
}

显然,该函数仅适用于模板化类型。