MRE:
std::vector<std::string> someFunction() {
auto vec;
return vec;
}
What’s stopping "auto" from inferring the type of vec as std::vector<std::string>?
>Solution :
From your question, it appears that you believe that since the compiler knows the return type, it should be able to deduce that the variable being returned must be of that return type. But, that is not how the compiler works!
Rather, the compiler knows the return type, so it tries to implicitly cast vec to that known return type upon returning. But, it doesn’t know how to perform this implicit cast because it doesn’t know the original type of vec.
That’s just how the compiler works. Specifying a return type doesn’t change the type of vec, it just ensures that the compiler tries to implicitly cast vec to that type.
So, with your code, you get this error when compiled with the GNU g++ compiler as C++17:
main.cpp: In function ‘std::vector<std::__cxx11::basic_string<char> > someFunction()’:
main.cpp:15:5: error: declaration of ‘auto vec’ has no initializer
15 | auto vec;
| ^~~~
To make my point, check this out. This code compiles! I am forcing a reinterpret cast from an int to a std::vector<std::string>:
Run this code online here: https://onlinegdb.com/RaDEmExfF
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> someFunction()
{
int i;
// return by value (copy)
return *((std::vector<std::string>*)(&i));
}
int main()
{
std::vector<std::string> vector = someFunction();
return 0;
}
Since that makes no sense and is totally undefined behavior, however, I get a run-time crash (on Linux x86-64):
terminate called after throwing an instance of 'std::bad_array_new_length'
what(): std::bad_array_new_length
Just remember, the return type doesn’t specify the source type of vec. Rather, it simply enforces the type that vec gets implicitly cast to when it is returned.
Going further: memory pools
Check this out. To make things even weirder for you, this is a totally fine and valid program now, with well-defined behavior.
I simply used an array of bytes as a memory pool to statically construct a std::vector<std::string> object from within a memory pool of 100 bytes. I could have used a memory pool of ints too, as int[25].
#include <iostream>
#include <string>
#include <vector>
std::vector<std::string> someFunction()
{
// undefined behavior check
constexpr uint16_t NUM_BYTES = 100;
static_assert(NUM_BYTES >= sizeof(std::vector<std::string>));
uint8_t memory_pool[NUM_BYTES];
// return by value (copy)
return *((std::vector<std::string>*)(memory_pool));
}
int main()
{
std::vector<std::string> vector = someFunction();
vector.push_back("hello ");
vector.push_back("world");
std::cout << vector[0] << vector[1] << "\n";
return 0;
}
Now it is a perfect program and runs just fine. The output is:
hello world
One more valid version of this function that now has optimal memory usage:
std::vector<std::string> someFunction()
{
uint8_t memory_pool[sizeof(std::vector<std::string>)];
// return by value (copy)
return *((std::vector<std::string>*)(memory_pool));
}
Or, just skip the memory pool and use the proper type in the first place:
std::vector<std::string> someFunction()
{
std::vector<std::string> vec;
return vec;
}