我的主要问题是:
如何正确编写应用结构化绑定并将结果变量传递给函数的函数,同时正确保留引用、常量性等...
在代码中
template<?>
<?> call_with( ? && object, ? && function ){
auto ? [ x1, x2, x3 ] = ?( object );
return ?( (?(function))( ?(x1), ?(x2), ?(x3) );
}
在这种情况下,我有以下小问题
(1),
auto [ x1, x2, x3 ] = ...
vs auto & [ x1, x2, x3 ] = ...
vs auto && [ x1, x2, x3 ] = ...
实际上有什么区别。在这种情况下应该使用哪个?
(2),如果
object
作为右值传递,那么将 x1
、x2
和 x3
也视为右值是否有意义? (对于可能的类型,他们可以有 T
、T &
、T &&
、T const &
等)。
类似的东西也适用于左值吗?这将如何实现?
(3),
function
的“正确”输入是什么?在示例中我将其作为转发参考。
(4)是否还需要进行其他操作?
object
移动或转发function
function
(5) 如何定义返回类型而不丢失
function
返回的潜在引用?
前言
这个答案是为了与
std::apply
一致,使得元组的元素通过std::apply
和call_with
以相同的方式转发。这似乎是解决这个问题的最明智的方法,因为两个函数的行为相似,但使用不同的机制(类似元组与结构化绑定)。
解决方案
template< typename T, typename U >
struct copy_cref { using type = U; };
template< typename T, typename U >
struct copy_cref< T &, U > { using type = U &; };
template< typename T, typename U >
struct copy_cref< T const &, U > { using type = U const &; };
template< typename T, typename U >
struct copy_cref< T &&, U > { using type = U &&; };
template< typename T, typename U >
struct copy_cref< T const &&, U > { using type = U const &&; };
template<typename T,typename U>
using copy_cref_t = typename copy_cref<T,U>::type;
template< typename F, typename T >
constexpr decltype(auto) call_with( F && f, T && t )
{
auto && [ x1, x2, x3, x4 ] = t;
return std::invoke(
std::forward< F >( f ),
std::forward< copy_cref_t< decltype( t ), decltype( x1 ) > >( x1 ),
std::forward< copy_cref_t< decltype( t ), decltype( x2 ) > >( x2 ),
std::forward< copy_cref_t< decltype( t ), decltype( x3 ) > >( x3 ),
std::forward< copy_cref_t< decltype( t ), decltype( x4 ) > >( x4 )
);
}
解释
std::apply
如果我们看一下
std::apply
的仅说明实现
template<class F,class Tuple, std::size_t... I>
constexpr decltype(auto)
apply-impl(F&& f, Tuple&& t, std::index_sequence<I...>) // exposition only
{
return INVOKE(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
我们看到通过在转发的元组上使用
INVOKE
将元素传递给std::get
。与此处相关的 std::get
声明是
template< std::size_t I, class... Types >
std::tuple_element<I, tuple<Types...>>::type& get( tuple<Types...>& t ) noexcept;
template< std::size_t I, class... Types >
std::tuple_element<I, tuple<Types...>>::type&& get( tuple<Types...>&& t ) noexcept;
template< std::size_t I, class... Types >
const std::tuple_element<I, tuple<Types...>>::type& get( const tuple<Types...>& t ) noexcept;
template< std::size_t I, class... Types >
const std::tuple_element<I, tuple<Types...>>::type&& get( const tuple<Types...>&& t ) noexcept;
观察
const
和引用只是从元组类型转移到元素类型。与首先删除引用然后设置引用的 std::forward_like
不同,它使用引用折叠规则 T && &
-> T &
。这是 copy_cref
实现的行为。
对于我的具体问题。
(1) 我仍然没有找到对差异的令人满意的解释。看来
auto && [...]
是最通用的版本了。
(2)
std::apply
采取的方法似乎是最安全的选择。我假设当您存储对存储在同一结构/元组中的数据的左值和右值引用时会出现问题。因此,答案是“视情况而定”。
(3,4,5) 不知道,我只是复制了
std::apply
。请参阅相关资源。
相关资源
https://en.cppreference.com/w/cpp/utility/apply
https://en.cppreference.com/w/cpp/utility/functions/invoke
https://en.cppreference.com/w/cpp/utility/tuple/get
结构化绑定和转发引用可以很好地混合吗?
何时使用 std::invoke 而不是简单地调用可调用对象?
从函数返回时,auto 和 decltype(auto) 有什么区别?