Is there a way to perform function signature matching based on parameter values?

In function-oriented languages ​​such as Haskell, you can overload function definitions with several axes of the parameter signature. C ++ supports the number and type of arguments. Other languages ​​support argument values ​​and even protective sentences (code that checks the arguments for conditions.) For example, factorial implementation in Haskell:

factorial :: (Integral a) => a -> a  
factorial 0 = 1  
factorial n = n * factorial (n - 1) 

If the definition of factorial when the argument is 0 is different from the definition of factorial when the argument is any other integer.

I did not find this feature in C ++ and thought it would be difficult to implement in the language. Further reflection made me think that in fact it would be a fairly easy and enjoyable addition to the language, so I should miss it.

Is there a way to do this, either in my own syntax or in templates?

+4
source share
6 answers

I think the real answer here is that there is no exact equivalent. Yet. The specialization of the template is close, but only works at compile time, which somewhat limits its usability. Of course, we have a branch, but this has limited power compared to the fact that pattern matching can be performed in other functional programming languages.

There is currently a proposal for pattern matching in C ++: P0095r1 , which would allow the use of the following factorial definition, assuming concepts:

template <Integral I>
I factorial(I n) {
    return inspect(n) {
        0 => 1
        n => n * factorial(n-1)
    };
}

, , , .

+2

, . , , .

//Main template definition
template<typename T>
void foo(T) { std::cout << "Some T\n"; }

//Specialization for int
template<>
void foo(int) { std::cout << "Called with an int!\n"; }

"" , - ( ):

template<std::size_t N>
struct factorial {
    static constexpr unsigned long long value = N * factorial<N - 1>::value;
};

template<>
struct factorial<0> {
    static constexpr unsigned long long value = 1;
}

auto foo = factorial<10>::value;

, ( switch/if) .

+2

,

//recursively calls itself until N is 1
template<int N>
struct factorial<N>{enum{value = N * factorial<N-1>::value};};

//at which point, this will be called (stopping the recursion)
template<>
struct factorial<1>{enum{value = 1};};

,

int factorial_recursion(int n){
  if(n == 1)
    return 1;
  else
    return n * factorial_recursion(n - 1);
}
//or
int factorial_loop(int n){
  int answer = 1;
  for(int count = n; count > 1; --count)
    answer *= count;

  return answer;
}
+1

: ++ Haskell. , Haskell , , . , .

/ : - , , -. ++ , , ​​. , -, , , . , post-initial-write.

0

if <condition> ? <if-true> : <if-false>.

, , , , .

sfinae:

template<int n, std::enable_if_t<(n > 1), short> = 0>
constexpr int factorial(std::integral_constant<int, n>) {
    return n * factorial(std::integral_constant<n - 1>{});
}

template<int n, std::enable_if_t<(n == 0), short> = 0>
constexpr int factorial(std::integral_constant<int, n>) { return 1; }

, enable_if_t. , , .

, . , , :

constexpr factorial(int n) {
    return n == 0 ? 1 : n * factorial(n - 1);
} 
0
int factorial(int n)
{
    switch(n)
    {
        case 0: return 1;
        default: return n * factorial(n - 1);
    }
}
0

Source: https://habr.com/ru/post/1654591/


All Articles