Context
When passing arguments to functions, we can either pass-by-reference or pass-by-value.
One advantage to passing by reference is that the argument does not need to be implemented via a copy of itself, and this can seem advantageous for custom objects that contain a lot of data.
But for multiple class interfaces, creating a pass-by-reference function for each interface can be… untidy e.g.:
void parse(Console&);
void parse(Dialog&);
void parse(Image&);
void parse(Window&);
void parse(...); // how many more overloads?!
So, on to templates to solve this untidy problem, right?
template <typename type> void parse(type&);
But I've hit a road bump…
Attempted Solution (Code)
#include <iostream>
class Object { public:
// Copy/ Move constructors implicitly defined;
// Same goes for the assignment operator.
constexpr inline Object(void) {}
// Test if the object can be a reference or is an expression value.
template <typename type>
inline static void parse(type) noexcept { std::cout << "[pass-by-value]" << std::endl; }
template <typename type>
inline static void parse(type&) noexcept { std::cout << "[pass-by-reference]" << std::endl; }
};
Passing the object by value works
Object::parse(Object()); // SUCCESS: "pass-by-value"
but using a reference causes the test function to conflict with its overloads.
Object object {};
Object::parse(object); // ERROR: Ambiguous call of `Object::parse` function.
// EXPECTED: "pass-by-reference"
I assume the call is ambiguous because the
parse(type)
overload is initialized with {type=Object&}
and the
parse(type&)
overload is initialized with {type=Object}
.
Although, I thought that the parse(type&)
overload would be preferred but apparently that doesn't seem to be the case.
So, what am I missing here?
Question
How can we differentiate between an argument that could be a reference or an argument that is a constant expression using a template function?
Note: I'm trying to prevent unnecessary copies with user-defined objects i.e.:
If I pass Image() [type=Image]
as an argument, it will be passed by value.
Otherwise it will be passed by reference e.g.:
Image image; // -> image [type=Image&]
Image images[1]; // -> images[0] [type=Image&]
Image *image_p; // -> *(image_p + 0) [type=Image&]