是否可以使用统一的解引用语法?

时间:2017-08-08 16:04:52

标签: c++ c++11 metaprogramming template-meta-programming

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

5 个答案:

答案 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";
}

live example

我最初是用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
}

godbolt

上查看

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

[live demo]