4

In functional oriented languages like Haskell, one can overload function definitions an several axis of parameter signature. C++ supports number and type of arguments. Other languages support argument values and even guard clauses (code that tests the arguments for conditions.) For instance the factorial implementation in Haskell:

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

Where the definition for factorial when the argument is 0 differs from the definition for factorial when the argument is any other integer.

I have not found this capability in C++ and thought, at first, that it would be difficult to implement in the language. Further reflection made me think it actually would be fairly easy and a nice addition to the language, so I must be missing it.

Is there any way to do this either in native syntax or templates?

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
Calcite
  • 131
  • 1
  • 6
  • C++ templates can be specialized on integer constants, which achieves roughly the same thing as your Haskell example. [Here is a factorial function written with specialized templates in C++](http://stackoverflow.com/q/3082113/464709). – Frédéric Hamidi Sep 14 '16 at 14:29
  • Yes, it can be done with template metaprogramming but only when the parameter values are known at compile time. C++ can't dispatch on parameter values at runtime, other than by virtual method dispatch. – antlersoft Sep 14 '16 at 14:32
  • That's not two overloaded functions, it's the function `factorial x = case x of 0 => 1; n => n * factorial (n - 1)` with syntactic sugar sprinkled on top. – molbdnilo Sep 14 '16 at 14:51

6 Answers6

2

There is such a thing, and it's called template specialization. Basically, you can define a template for a given type apart from the general template definition. You can read about it here

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

The factorial template "function" also uses template specialization, but due to the nature of templates, it can only calculate compile time values (template metaprogramming):

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;

As far as I know, there is no such thing at run time (except for switch/if branches) in a given function.

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
2

I think the real answer here is that there isn't an exact equivalent. Yet. Template specialization is close, but that only works at compile time, which several limits its usability. We have branching of course, but that has limited power compared to what pattern matching can do in other functional programming languages.

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

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

I'm not totally sure on the syntax, but then again, it's just a proposal so far, so the syntax itself could change.

Barry
  • 286,269
  • 29
  • 621
  • 977
1

If the values are known at compile time, it can be done with templates

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

If the values are only known at runtime, the decision must be done at runtime

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;
}
Altainia
  • 1,347
  • 1
  • 7
  • 11
0

Short answer: no C++ does not have Haskell-style pattern matching. Also worth mentioning that in the Haskell example, you have a single function, rather than two of them, but you just have a nicer syntax for checking the values of inputs. In overloading, you actually have two or more functions with the same name, but different number or types of arguments.

Longer/truthier answer: something akin to what you are suggesting is possible via template-metaprogramming. Since C++ templates allow values as template arguments, and not only types, you can actually build such a function. The template-language is apparently Turing complete, so you can actually compute everything that's computable with it. Of course, it looks terrible and leads to massive compilation times and difficulty in understanding the code post-initial-write.

Horia Coman
  • 8,681
  • 2
  • 23
  • 25
0

Runtime branching is done using if or the ternary operator <condition> ? <if-true> : <if-false>.

Overloads of a function is done at compile time, so it means that if you want to choose an overload of a function based on values, you must know that value strictly at compile time.

Here's an example of compile time branching using 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; }

Here, notice the conditions used in enable_if_t. It will make the function invalid to call if the condition is not met, and force the compiler to try alternative functions.

Of course, the syntax is not that great. The best would be to have a single implementation for both runtime and compile time, but for that you must using traditional branching:

constexpr factorial(int n) {
    return n == 0 ? 1 : n * factorial(n - 1);
} 
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
0
int factorial(int n)
{
    switch(n)
    {
        case 0: return 1;
        default: return n * factorial(n - 1);
    }
}
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • My thought was that the compiler would/could/might create this "under-the-covers". – Calcite Sep 15 '16 at 17:00
  • @JasonDoege I think c++ is all about explicitness. Having the compiler getting up to tricks behind people's backs would I think be against the ethos of the language. I know people like Haskell, but to me the syntax is extremely cryptic and (other than this trivial case) extremely difficult to reason about. I don't know maybe it's just that I'm an old machine code hacker at heart :) – Richard Hodges Sep 15 '16 at 17:36