[英]std::make_shared inside std::apply
考慮以下代碼示例。
它創建類 A(帶有模板參數T
和Args...
)並將其構造函數參數保存到元組args_
。 稍后它使用args_
在get
方法中創建T
的實例。
一切正常,除了如您所見,我確實在std::apply
使用了my_own_make_shared
函數,因為如果我用std::make_shared
替換它,我將無法編譯此代碼。 任何人都知道問題是什么?
#include <iostream>
#include <charconv>
#include <array>
#include <cstring>
#include <iostream>
#include <tuple>
#include <utility>
#include <memory>
#include <algorithm>
struct Base
{
virtual void foo() = 0;
};
struct Test : Base
{
Test(int, int) {}
void foo() override {std::cout << "Test\n";}
};
struct Test2 : Base
{
Test2(int, int, const std::string&) {}
void foo() override {std::cout << "Test2\n";}
};
template<typename T, typename... Args>
std::shared_ptr<T> my_own_make_shared(Args... args)
{
return std::make_shared<T>(args...);
}
template <typename T, typename... Args>
struct A
{
A(Args... args) : args_(std::make_tuple(args...)) {}
std::shared_ptr<Base> get()
{
return std::apply(my_own_make_shared<T, Args...>, args_);
}
std::tuple<Args...> args_;
};
template <typename T, typename... Args>
auto make_A(Args... args)
{
return A<T, Args...>(args...);
}
int main(int argc, char** argv)
{
make_A<Test>(1, 2).get()->foo();
make_A<Test2>(1, 2, "").get()->foo();
}
編譯器錯誤 (GCC-9)。 假設我們用std::make_shared
替換了my_own_make_shared
In file included from test.cpp:6:
/usr/include/c++/9/tuple: In instantiation of ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::shared_ptr<Test> (&)(int&&, int&&); _Tuple = std::tuple<int, int>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]’:
/usr/include/c++/9/tuple:1694:31: required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::shared_ptr<Test> (&)(int&&, int&&); _Tuple = std::tuple<int, int>&]’
test.cpp:41:26: required from ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test; Args = {int, int}]’
test.cpp:55:28: required from here
/usr/include/c++/9/tuple:1684:27: error: no matching function for call to ‘__invoke(std::shared_ptr<Test> (&)(int&&, int&&), std::__tuple_element_t<0, std::tuple<int, int> >&, std::__tuple_element_t<1, std::tuple<int, int> >&)’
1684 | return std::__invoke(std::forward<_Fn>(__f),
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
1685 | std::get<_Idx>(std::forward<_Tuple>(__t))...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/9/tuple:41,
from test.cpp:6:
/usr/include/c++/9/bits/invoke.h:89:5: note: candidate: ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)’
89 | __invoke(_Callable&& __fn, _Args&&... __args)
| ^~~~~~~~
/usr/include/c++/9/bits/invoke.h:89:5: note: template argument deduction/substitution failed:
/usr/include/c++/9/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = std::shared_ptr<Test> (&)(int&&, int&&); _Args = {int&, int&}]’:
/usr/include/c++/9/tuple:1684:27: required from ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::shared_ptr<Test> (&)(int&&, int&&); _Tuple = std::tuple<int, int>&; long unsigned int ..._Idx = {0, 1}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1>]’
/usr/include/c++/9/tuple:1694:31: required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::shared_ptr<Test> (&)(int&&, int&&); _Tuple = std::tuple<int, int>&]’
test.cpp:41:26: required from ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test; Args = {int, int}]’
test.cpp:55:28: required from here
/usr/include/c++/9/bits/invoke.h:89:5: error: no type named ‘type’ in ‘struct std::__invoke_result<std::shared_ptr<Test> (&)(int&&, int&&), int&, int&>’
test.cpp: In instantiation of ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test; Args = {int, int}]’:
test.cpp:55:28: required from here
test.cpp:41:26: error: could not convert ‘std::apply<std::shared_ptr<Test> (&)(int&&, int&&), std::tuple<int, int>&>(std::make_shared<Test, int, int>, ((A<Test, int, int>*)this)->A<Test, int, int>::args_)’ from ‘void’ to ‘std::shared_ptr<Base>’
41 | return std::apply(std::make_shared<T, Args...>, args_);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| void
In file included from test.cpp:6:
/usr/include/c++/9/tuple: In instantiation of ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&); _Tuple = std::tuple<int, int, const char*>&; long unsigned int ..._Idx = {0, 1, 2}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1, 2>]’:
/usr/include/c++/9/tuple:1694:31: required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&); _Tuple = std::tuple<int, int, const char*>&]’
test.cpp:41:26: required from ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test2; Args = {int, int, const char*}]’
test.cpp:56:33: required from here
/usr/include/c++/9/tuple:1684:27: error: no matching function for call to ‘__invoke(std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&), std::__tuple_element_t<0, std::tuple<int, int, const char*> >&, std::__tuple_element_t<1, std::tuple<int, int, const char*> >&, const char*&)’
1684 | return std::__invoke(std::forward<_Fn>(__f),
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
1685 | std::get<_Idx>(std::forward<_Tuple>(__t))...);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/9/tuple:41,
from test.cpp:6:
/usr/include/c++/9/bits/invoke.h:89:5: note: candidate: ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...)’
89 | __invoke(_Callable&& __fn, _Args&&... __args)
| ^~~~~~~~
/usr/include/c++/9/bits/invoke.h:89:5: note: template argument deduction/substitution failed:
/usr/include/c++/9/bits/invoke.h: In substitution of ‘template<class _Callable, class ... _Args> constexpr typename std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, _Args&& ...) [with _Callable = std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&); _Args = {int&, int&, const char*&}]’:
/usr/include/c++/9/tuple:1684:27: required from ‘constexpr decltype(auto) std::__apply_impl(_Fn&&, _Tuple&&, std::index_sequence<_Idx ...>) [with _Fn = std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&); _Tuple = std::tuple<int, int, const char*>&; long unsigned int ..._Idx = {0, 1, 2}; std::index_sequence<_Idx ...> = std::integer_sequence<long unsigned int, 0, 1, 2>]’
/usr/include/c++/9/tuple:1694:31: required from ‘constexpr decltype(auto) std::apply(_Fn&&, _Tuple&&) [with _Fn = std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&); _Tuple = std::tuple<int, int, const char*>&]’
test.cpp:41:26: required from ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test2; Args = {int, int, const char*}]’
test.cpp:56:33: required from here
/usr/include/c++/9/bits/invoke.h:89:5: error: no type named ‘type’ in ‘struct std::__invoke_result<std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&), int&, int&, const char*&>’
test.cpp: In instantiation of ‘std::shared_ptr<Base> A<T, Args>::get() [with T = Test2; Args = {int, int, const char*}]’:
test.cpp:56:33: required from here
test.cpp:41:26: error: could not convert ‘std::apply<std::shared_ptr<Test2> (&)(int&&, int&&, const char*&&), std::tuple<int, int, const char*>&>(std::make_shared<Test2, int, int, const char*>, ((A<Test2, int, int, const char*>*)this)->A<Test2, int, int, const char*>::args_)’ from ‘void’ to ‘std::shared_ptr<Base>’
41 | return std::apply(std::make_shared<T, Args...>, args_);
| ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| void
下面是從參考中獲取的std::apply
實現
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Tuple&& t, std::index_sequence<I...>)
{
// This implementation is valid since C++20 (via P1065R2)
// In C++17, a constexpr counterpart of std::invoke is actually needed here
return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
} // namespace detail
正如我們所看到的,函子及其參數作為元組的元素被轉發。
在你的情況下args_
是 Lvaue。 所以通過std::get<I>(std::forward<Tuple>(t))...
你得到了對特定元組元素的左值引用。
為了
make_A<Test>(1, 2).get()->foo();
參數包Args...
推導出為: {int,int}
和以下內容
return std::apply(std::make_shared<T, Args...>, args_);
無法編譯,因為R 值引用不能綁定到 Lvalues 。 R 值引用出現在make_shared
參數列表中。 您使用此重載:
template< class T, class... Args >
shared_ptr<T> make_shared( Args&&... args );
其中Args...
是{int,int}
,所以參數列表是int&&, int&&
。
如果您想使用元組的元素作為 apply as Lvalue 的參數,只需將Args 轉換為 Lvalues:
return std::apply(std::make_shared<T, std::add_lvalue_reference_t<Args>...>, args_);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.