`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:
问题
- 有人能解释一下这是怎么回事吗?
- 有谁知道如何进行这项工作?
正确的语法是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");
}
显然,该函数仅适用于模板化类型。
背景
我有一个可变 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:
问题
- 有人能解释一下这是怎么回事吗?
- 有谁知道如何进行这项工作?
正确的语法是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");
}
显然,该函数仅适用于模板化类型。