统一取消引用语法是否可行?



>假设我有一个函数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();

换句话说,我想要的是一个统一的取消引用语法,由一个通用的、多级的、智能的取消引用函数(或其他允许统一取消引用语法的东西(提供支持,deep_deref()它将取消引用传递给它的对象,然后是从该取消引用中获得的对象,然后是下一个,依此类推,直到它到达一个不可取消引用的对象, 此时它将返回最终对象。

请注意,沿着这条去引用的路径,可能存在各种可取消引用的对象:指针、迭代器、智能指针等——任何可取消引用的对象。

这样的事情可能吗?(假设我有is_dereferencable

使用std::decay通过取消引用和删除 CV 限定符来创建can_dereference函数,这是可以做到的。

链接到此处的答案是完整的实现以及实时示例。

我最初将其作为评论发布,但认为答案会更好地帮助人们搜索

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); }

这是未经测试且不完整的。 你应该使用&&和转发等等。 很多清理和重新订购要做...

我也质疑拥有这样的东西是否明智。

一些样板:

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{};

} 模板类 Z,类...Ts>使用 can_apply = 详细信息::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中,它要干净得多。

对于可以使用较新版本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
}

在 godbolt 检查它

我喜欢保持简单...我会像这样实现它:

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{});
}

[现场演示]

相关内容

最新更新