代码之家  ›  专栏  ›  技术社区  ›  Arnaud

有可能阻止普遍引用吗[[副本]

  •  0
  • Arnaud  · 技术社区  · 5 年前

    我正在编写一个网络库,并大量使用移动语义来处理文件描述符的所有权。我的一个类希望接收其他类型的文件描述符包装器并获得所有权,因此类似于

    struct OwnershipReceiver
    {
      template <typename T>
      void receive_ownership(T&& t)
      {
         // taking file descriptor of t, and clear t
      }
    };
    

    它必须处理多个不相关的类型,所以receive\ u所有权必须是一个模板,而且为了安全起见,我希望它只绑定到rvalue引用,这样用户在传递左值时必须显式地声明std::move。

    receive_ownership(std::move(some_lvalue));

    所以问题是:如何使模板只绑定到rvalue引用?

    0 回复  |  直到 13 年前
        1
  •  40
  •   Howard Hinnant    13 年前

    你可以限制 T 不作为左值引用,从而防止左值绑定到它:

    #include <type_traits>
    
    struct OwnershipReceiver
    {
      template <typename T,
                class = typename std::enable_if
                <
                    !std::is_lvalue_reference<T>::value
                >::type
               >
      void receive_ownership(T&& t)
      {
         // taking file descriptor of t, and clear t
      }
    };
    

    T

        2
  •  14
  •   Toby Speight    5 年前

    已删除成员

    template<typename T> void receive_ownership(T&) = delete;
    

    对于左值参数来说,这总是一个更好的匹配。


    #include <type_traits>
    
    template<typename T>
    void receive_ownership(T&& t)
        requires !std::is_lvalue_reference<T>::value
    {
         // taking file descriptor of t, and clear t
    }
    

    #include <type_traits>
    
    void receive_ownership(auto&& t)
        requires std::is_rvalue_reference<decltype(t)>::value
    {
         // taking file descriptor of t, and clear t
    }
    

    #include <type_traits>
    
    template<typename T>
    concept bool rvalue = std::is_rvalue_reference<T&&>::value;
    
    
    void receive_ownership(rvalue&& t)
    {
         // taking file descriptor of t, and clear t
    }
    

    注意:对于GCC6.1,您需要通过 -fconcepts

    为了完整起见,下面是我的简单测试:

    #include <utility>
    int main()
    {
        int a = 0;
        receive_ownership(a);       // error
        receive_ownership(std::move(a)); // okay
    
        const int b = 0;
        receive_ownership(b);       // error
        receive_ownership(std::move(b)); // allowed - but unwise
    }
    
        3
  •  5
  •   Toby Speight    5 年前

    我学到了一些似乎经常让人困惑的东西:使用SFINAE是可以的,但我不能使用:

    std::is_rvalue_reference<T>::value
    

    我想要的唯一办法就是

    !std::is_lvalue_reference<T>::value
    

    R值 ,不是 R值 . 有条件地启用的函数 std::is_rvalue_reference<T>::value 将不接收右值,而是接收右值引用。

        4
  •  2
  •   Hugo LEFEBVRE    8 年前

    对于左值引用,T被推导出为左值引用,对于右值引用,T被推导出为非引用。

    因此,如果函数绑定到一个rvalue引用,编译器在最后看到的特定类型T是:

    std::is_rvalue_reference<T>::value

    而不是

    std::is_rvalue_reference<T&&>::value

        5
  •  0
  •   Eric Cousineau    6 年前

    is_rvalue_reference<TF> TF 如果您实际尝试使重载区分 const T& T&& enable_if is_rvalue_reference_v<TF> 另一个是 !is_rvalue_reference_V<TF> ).

    T ,然后将重载放在知道这些类型的容器中。生成 this example

    哈,我错了,只是忘了看托比的答案( is_rvalue_reference<TF&&> std::forward<TF>(...) 但我想这就是原因 decltype(arg) 同样有效。

    struct 过载,(2)使用错误的检查 is_rvalue_reference ,和(3)正确的检查:

    /*
    Output:
    
    const T& (struct)
    const T& (sfinae)
    const T& (sfinae bad)
    ---
    const T& (struct)
    const T& (sfinae)
    const T& (sfinae bad)
    ---
    T&& (struct)
    T&& (sfinae)
    const T& (sfinae bad)
    ---
    T&& (struct)
    T&& (sfinae)
    const T& (sfinae bad)
    ---
    */
    
    #include <iostream>
    #include <type_traits>
    
    using namespace std;
    
    struct Value {};
    
    template <typename T>
    struct greedy_struct {
      static void run(const T&) {
        cout << "const T& (struct)" << endl;
      }
      static void run(T&&) {
        cout << "T&& (struct)" << endl;
      }
    };
    
    // Per Toby's answer.
    template <typename T>
    void greedy_sfinae(const T&) {
      cout << "const T& (sfinae)" << endl;
    }
    
    template <
        typename T,
        typename = std::enable_if_t<std::is_rvalue_reference<T&&>::value>>
    void greedy_sfinae(T&&) {
      cout << "T&& (sfinae)" << endl;
    }
    
    // Bad.
    template <typename T>
    void greedy_sfinae_bad(const T&) {
      cout << "const T& (sfinae bad)" << endl;
    }
    
    template <
        typename T,
        typename = std::enable_if_t<std::is_rvalue_reference<T>::value>>
    void greedy_sfinae_bad(T&&) {
      cout << "T&& (sfinae bad)" << endl;
    }
    
    template <typename TF>
    void greedy(TF&& value) {
      using T = std::decay_t<TF>;
      greedy_struct<T>::run(std::forward<TF>(value));
      greedy_sfinae(std::forward<TF>(value));
      greedy_sfinae_bad(std::forward<TF>(value));
      cout << "---" << endl;
    }
    
    int main() {
      Value x;
      const Value y;
    
      greedy(x);
      greedy(y);
      greedy(Value{});
      greedy(std::move(x));
    
      return 0;
    }