Why doesn’t C++ support functions returning arrays?

Spread the love

Question Description

Some languages enable you to just declare a function returning an array like a normal function, like Java:

public String[] funcarray() {
   String[] test = new String[]{"hi", "hello"};
   return test;
}

Why doesn’t C++ support something like int[] funcarray(){} ?
You can return an array, but it’s a real hassle to make such a function. And also, I heard somewhere that strings are just arrays of char. So if you can return a string in C++, why not an array?

Practice As Follows

I’d wager a guess that to be concise, it was simply a design decision. More specifically, if you really want to know why, you need to work from the ground up.

Let’s think about C first. In the C language, there is a clear distinction between “pass by reference” and “pass by value”. To treat it lightly, the name of an array in C is really just a pointer. For all intents and purposes, the difference (generally) comes down to allocation. The code

int array[n];

would create 4*n bytes of memory (on a 32 bit system) on the stack correlating to the scope of whichever code block makes the declaration. In turn,

int* array = (int*) malloc(sizeof(int)*n);

would create the same amount memory, but on the heap. In this case, what is in that memory isn’t tied to the scope, only the reference TO the memory is limited by the scope. Here’s where pass by value and pass by reference come in. Passing by value, as you probably know, means that when something is passed in to or returned from a function, the “thing” that gets passed is the result of evaluating the variable. In other words,

int n = 4;
printf("%d", n);

will print the number 4 because the construct n evaluates to 4 (sorry if this is elementary, I just want to cover all the bases). This 4 has absolutely no bearing or relationship to the memory space of your program, it’s just a literal, and so once you leave the scope in which that 4 has context, you lose it. What about pass by reference? Passing by reference is no different in the context of a function; you simply evaluate the construct that gets passed. The only difference is that after evaluating the passed “thing”, you use the result of the evaluation as a memory address. I once had a particular cynical CS instructor who loved to state that there is no such thing as passing by reference, just a way to pass clever values. Really, he’s right. So now we think about scope in terms of a function. Pretend that you can have an array return type:

int[] foo(args){
    result[n];
    // Some code
    return result;
}

The problem here is that result evaluates to the address of the 0th element of the array. But when you attempt to access this memory from outside of this function (via the return value), you have a problem because you are attempting to access memory that is not in the scope with which you are working (the function call’s stack). So the way we get around this is with the standard “pass by reference” jiggery-pokery:

int* foo(args){
    int* result = (int*) malloc(sizeof(int)*n));
    // Some code
    return result;
}

We still get a memory address pointing to the 0th element of the Array, but now we have access to that memory.

What’s my point? In Java, it is common to assert that “everything is pass by value”. This is true. The same cynical instructor from above also had this to say about Java and OOP in general: Everything is just a pointer. And he’s also right. While everything in Java is in fact pass by value, almost all of those values are actually memory addresses. So in Java, the language does let you return an array or a String, but it does so by turning it in to the version with pointers for you. It also manages your memory for you. And automatic memory management, while helpful, is not efficient.

This brings us to C++. The whole reason C++ was invented was because Bjarne Stroustrup had been experimenting with Simula (basically the original OOPL) during his PhD work, and thought it was fantastic conceptually, but he noticed that it performed rather terribly. And so he began working on what was called C with Classes, which got renamed to C++. In doing so, his goal was to make a programming language that took SOME of the best features from Simula but remained powerful and fast. He chose to extend C due to its already legendary performance, and one tradeoff was that he chose to not implement automatic memory management or garbage collecting on such a large scale like other OOPL’s. Returning an array from one of the template classes works because, well, you’re using a class. But if you want to return a C array, you have to do it the C way. In other words, C++ does support returning an array EXACTLY the same way that Java does; it just doesn’t do all of the work for you. Because a Danish dude thought it’d be too slow.