如何使用 `requires` 和 `-> return_type` 声明成员函数

How to Declar Member Function with both `requires` and `-> return_type`

我想写一个class模板,其中:

我想明确说明 make_unsigned 的 return 值。这是我的代码。

#include <concepts>

template<typename T> struct A
{
    T val;
    std::make_unsigned_t<T> make_unsigned() requires std::signed_integral<T>
    {
        return static_cast<std::make_unsigned<T>::type>(val);
    }
};


int main()
{
    A<float> val;
    return 0;
}

我也试过auto make_unsigned() requires std::signed_integral<T> -> std::make_unsigned_t<T>auto make_unsigned() -> std::make_unsigned_t<T> requires std::signed_integral<T>,但是都编译不出来。它抱怨:

~ LANG=C g++ main.cpp -std=c++20 -o main
In file included from /usr/include/c++/10.2.0/bits/move.h:57,
                 from /usr/include/c++/10.2.0/bits/stl_pair.h:59,
                 from /usr/include/c++/10.2.0/utility:70,
                 from /usr/include/c++/10.2.0/array:38,
                 from main.cpp:1:
/usr/include/c++/10.2.0/type_traits: In instantiation of ‘struct std::make_unsigned<float>’:
/usr/include/c++/10.2.0/type_traits:1965:11:   required by substitution of ‘template<class _Tp> using make_unsigned_t = typename std::make_unsigned::type [with _Tp = float]’
main.cpp:8:29:   required from ‘struct A<float>’
main.cpp:17:14:   required from here
/usr/include/c++/10.2.0/type_traits:1826:62: error: invalid use of incomplete type ‘class std::__make_unsigned_selector<float, false, false>’
 1826 |     { typedef typename __make_unsigned_selector<_Tp>::__type type; };
      |                                                              ^~~~
/usr/include/c++/10.2.0/type_traits:1733:11: note: declaration of ‘class std::__make_unsigned_selector<float, false, false>’
 1733 |     class __make_unsigned_selector;

或:

~ LANG=C g++ main.cpp -std=c++20 -o main
main.cpp:8:59: error: base operand of ‘->’ is not a pointer
    8 |     auto make_unsigned() requires std::signed_integral<T> -> std::make_unsigned_t<T>
      |                                                           ^~
main.cpp:8:62: error: invalid use of ‘using make_unsigned_t = typename std::make_unsigned<_Tp>::type’
    8 |     auto make_unsigned() requires std::signed_integral<T> -> std::make_unsigned_t<T>
      |                                                              ^~~
cc1plus: error: expression must be enclosed in parenthese

或:

~ LANG=C g++ main.cpp -std=c++20 -o main
In file included from /usr/include/c++/10.2.0/bits/move.h:57,
                 from /usr/include/c++/10.2.0/bits/stl_pair.h:59,
                 from /usr/include/c++/10.2.0/utility:70,
                 from /usr/include/c++/10.2.0/array:38,
                 from main.cpp:1:
/usr/include/c++/10.2.0/type_traits: In instantiation of ‘struct std::make_unsigned<float>’:
/usr/include/c++/10.2.0/type_traits:1965:11:   required by substitution of ‘template<class _Tp> using make_unsigned_t = typename std::make_unsigned::type [with _Tp = float]’
main.cpp:8:10:   required from ‘struct A<float>’
main.cpp:17:14:   required from here
/usr/include/c++/10.2.0/type_traits:1826:62: error: invalid use of incomplete type ‘class std::__make_unsigned_selector<float, false, false>’
 1826 |     { typedef typename __make_unsigned_selector<_Tp>::__type type; };
      |                                                              ^~~~
/usr/include/c++/10.2.0/type_traits:1733:11: note: declaration of ‘class std::__make_unsigned_selector<float, false, false>’
 1733 |     class __make_unsigned_selector;

我该怎么做?

std::make_unsigned 对 SFINAE 不友好,所以你必须

  • 不要在 return 类型上显式,使用 auto/decltype(auto):

    auto make_unsigned() requires std::signed_integral<T>
    {
        return static_cast<std::make_unsigned<T>::type>(val);
    }
    
  • 添加一个“友好的”特征(一个不会使程序格式错误的特征,并且类型无效),另外使用 return 类型(因为函数不是模板, 这里没有 SFINAE):

    template <typename T>
    struct make_unsigned_or_self
    {
        using type = T;    
    };
    template <typename T>
    using make_unsigned_or_self_t = typename make_unsigned_or_self<T>::type;
    
    template <std::signed_integral T>
    struct make_unsigned_or_self<T>
    {
         using type = std::make_unsigned_t<T>;
    };
    

    然后

    make_unsigned_or_self_t<T> make_unsigned() requires std::signed_integral<T>
    {
        return static_cast<std::make_unsigned<T>::type>(val);
    }
    

    Demo