假设我有一个带有函数f()
的类型:
struct A { void f() {} };
和两个载体:
std::vector<A*> x;
std::vector<A*******> y;
(愚蠢的指针数量只是为了产生戏剧效果。)
我正在寻找一种能够写作的方法:
deep_deref(std::begin(x)).f();
deep_deref(std::begin(y)).f();
换句话说,我想要的是一个统一解除引用语法,由一个通用的,多层次的,智能解除引用功能(或其他允许统一解除引用语法的东西)提供支持{{1}将取消引用传递给它的对象,然后取消引用从该取消引用的对象,然后是下一个,等等,直到它到达一个不可解除引用的对象,此时它将返回最终的对象。
请注意,沿着这种解除引用的路径,可能存在各种可解除引用的对象:指针,迭代器,智能指针等 - 任何不可引用的东西。
这样的事情可能吗? (假设我有deep_deref()
。)
答案 0 :(得分:1)
template < typename T, bool isDeref = is_dereferenceable<T>::value >
struct deref_
{
static T& call(T & t) { return t; }
};
template < typename T >
struct deref_<T,true>
{
static decltype(*declval<T>()) call(T & t) { return deref_<decltype(*t)>::call(*t); }
};
template < typename T >
auto deref(T & t) { return deref_<T>::call(t); }
这是未经测试且不完整的。你应该使用&amp;&amp;以及所有这一切。很多清理和重新订购......
我也质疑这样的事情的智慧。
答案 1 :(得分:1)
使用std::decay
通过解除引用和删除CV限定符来创建can_dereference
函数,可以这样做。
与here相关联的答案是完整的实施以及实时样本。
我最初将此作为评论发布,但认为答案会更好地帮助人们搜索
答案 2 :(得分:0)
一些样板:
namespace details {
template<template<class...>class, class, class...>
struct can_apply:std::false_type{};
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class... Ts>
struct can_apply<Z, void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
templateclass Z,class ... Ts&gt;
使用can_apply = details :: can_apply;
确定是否应*
:
template<class T>
using unary_star_r = decltype( *std::declval<T>() );
template<class T>
using can_unary_star = can_apply<unary_star_r, T>;
dispatch
在编译时需要两个参数并在它们之间选择:
template<bool /*false*/>
struct dispatch_t {
template<class T, class F>
F operator()(T, F f)const{ return std::move(f); }
};
template<>
struct dispatch_t<true> {
template<class T, class F>
T operator()(T t, F)const{ return std::move(t); }
};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__))\
->decltype(__VA_ARGS__)\
{ return __VA_ARGS__; }
template<bool b, class T, class F>
auto
dispatch( T t, F f )
RETURNS( dispatch_t<b>{}( std::move(t), std::move(f) ) )
我们差不多完成了......
现在我们的工作。我们编写的函数对象代表解除引用类型,什么也不做,也许可以做任何一种:
struct maybe_deref_t;
struct do_deref_t;
struct identity_t {
template<class T>
T operator()(T&& t)const { return std::forward<T>(t); }
};
struct do_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS( maybe_deref_t{}( *std::forward<T>(t) ) )
};
这是工作:
struct maybe_deref_t {
template<class T>
auto operator()(T&& t)const
RETURNS(
dispatch< can_unary_star<T>::value >(
do_deref_t{},
identity_t{}
)(
std::forward<T>(t)
)
)
};
以及更好语法的帮助器:
template<class T>
auto maybe_deref( T&& t )
RETURNS( maybe_deref_t{}( std::forward<T>(t) ) )
测试代码:
int main() {
auto bob = new int*( new int(7) ); // or 0 or whatever
std::cout << maybe_deref(bob) << "\n";
}
我最初是用C ++ 14风格编写的,然后将其反向翻译为C ++ 11。在C ++ 14中,它更清晰。
答案 3 :(得分:0)
对于任何可以使用更新版本的C ++的人来说:
#include <utility>
namespace detail
{
struct Rank_0 {};
struct Rank_1 : Rank_0{}; // disambiguate overloads
template <class T, std::void_t<decltype(*std::declval<T>())>* = nullptr>
decltype(auto) deep_deref_impl(T& obj, Rank_1)
{
return deep_deref_impl(*obj, Rank_1{});
}
template <class T>
decltype(auto) deep_deref_impl(T& obj, Rank_0)
{
return obj;
}
}
template <class T>
decltype(auto) deep_deref(T& obj)
{
return detail::deep_deref_impl(obj, detail::Rank_1{});
}
auto test()
{
int a = 24;
int* p1 = &a;
int** p2 = &p1;
int*** p3 = &p2;
int**** p4 = &p3;
deep_deref(a) += 5;
deep_deref(p4) += 11;
return a; // 40
}
上查看
答案 4 :(得分:0)
我喜欢简单的事情......我实现它是这样的:
template <class T>
auto deep_deref_impl(T&& t, int) -> decltype(deep_deref_impl(*t, int{})) {
return deep_deref_impl(*t, int{});
}
template <class T>
T &deep_deref_impl(T&& t, ...) {
return t;
}
template <class T>
auto deep_deref(T&& t) -> decltype(deep_deref_impl(std::forward<T>(t), int{})) {
return deep_deref_impl(std::forward<T>(t), int{});
}