代码之家  ›  专栏  ›  技术社区  ›  Leo Heinsaar

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

  •  2
  • Leo Heinsaar  · 技术社区  · 9 年前

    假设我有一个带函数的类型 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

    5 回复  |  直到 9 年前
        1
  •  1
  •   Pruthvikar    9 年前

    使用 std::decay 创建 can_dereference 通过取消引用和删除CV限定符,可以实现这一点。

    here 是一个完整的实现和一个实时示例。

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

        2
  •  1
  •   Edward Strange    9 年前
    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); }
    

    这是未经测试和不完整的。您应该使用&&向前等等。许多清理和重新排序要做。。。

    我也质疑有这样的东西在身边是否明智。

        3
  •  0
  •   Yakk - Adam Nevraumont    9 年前

    一些样板:

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

    模板class Z,class…Ts> 决定你是否应该 *

    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中,它要干净得多。

        4
  •  0
  •   bolov    9 年前

    对于任何可以使用最新版本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

        5
  •  0
  •   W.F.    9 年前

    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]