Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I want to write a class template, which:

  • A<float> have just a member variable val;
  • A<int> have a member variable val and a member function make_unsigned (return unsigned).

I want to explicitly state the return value of make_unsigned. Here is my code.

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

Also I have tried auto make_unsigned() requires std::signed_integral<T> -> std::make_unsigned_t<T> and auto make_unsigned() -> std::make_unsigned_t<T> requires std::signed_integral<T>, but all of them cannot be compiled. It complains:

~ 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;

Or:

~ 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

Or:

~ 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;

How can I do it?

like image 487
陈浩南 Avatar asked Jan 13 '21 05:01

陈浩南


People also ask

How do you define a member function?

Member functions are operators and functions that are declared as members of a class. Member functions do not include operators and functions declared with the friend specifier. These are called friends of a class. You can declare a member function as static ; this is called a static member function.

How do you declare member function outside class?

If a member function's definition is outside the class declaration, it is treated as an inline function only if it is explicitly declared as inline . In addition, the function name in the definition must be qualified with its class name using the scope-resolution operator ( :: ).

How do you define a private member function in C++?

Private: The class members declared as private can be accessed only by the member functions inside the class. They are not allowed to be accessed directly by any object or function outside the class. Only the member functions or the friend functions are allowed to access the private data members of the class.


1 Answers

std::make_unsigned is not SFINAE friendly, so you have to

  • don't be explicit on return type, use auto/decltype(auto):

    auto make_unsigned() requires std::signed_integral<T>
    {
        return static_cast<std::make_unsigned<T>::type>(val);
    }
    
  • add a "friendly" trait (a trait which doesn't make the program ill-formed with invalid type), and additionally use a return type (as function is not template, no SFINAE here):

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

    and then

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

    Demo

like image 60
Jarod42 Avatar answered Oct 17 '22 11:10

Jarod42