I'm currently trying to decide whether to "structify" a rather long parameter set:
void fooCopy1(std::string const& source, std::string const& destination, std::string const& filter, std::string const& temp);
to this:
struct FooCopyArgs {
std::string source;
std::string destination;
std::string filter;
std::string temp;
};
void fooCopy2(FooCopyArgs const& args);
As already answered in two other questions:
- C++ using struct arguments for functions instead of multiple arguments?
- Why pass structure to function instead of separate parameters?
refactoring this could have several readability/maintainability advantages. For a bunch of these see the linked questions.
In general however I see one "big" problem with this approach in C++ specifically, and that is that the strings will always have to be copied before the call to this function, instead of using const& parameters.
This would be against C++ Core Guideline F.16 "pass cheaply-copied types by value and others by reference to const".
That is, non-cheap readonly parameters that would normally be passed by const ref, would need to be copied into the struct, which would be a general pessimization.
(Yes, the struct itself would be passed by const ref, but the struct data members would need to copied first.)
Example:
const string temp = ...;
const string filter = ...;
...
fooCopy2({"sourceItem", "targetItem", filter, temp});
For "sourceItem", that is a locally defined parameter value, it would not matter.
However, for the passed down args filterand temp we would have an extraneous copy that could be avoided by the plain const& approach.
Disclaimer: Obviously, in 99% of cases the performance impact won't even be observable in the final application, but still it leaves a bad taste, esp. in the context of some such "fundamental" rule as F.16.
Question : Is there any clever way around this problem, that is:
- have a safe struct as parameter type (
const&members are not safe; extremely prone to dangling references) - avoid extraneous copy of non-cheap types
- keep composability if severeal functions use this pattern
Appendix: Why using const& members is unsafe:
struct HasConstRef {
std::string const& member;
};
void f(HasConstRef const& arg) {
std::cout << arg.member << "\n";
}
HasConstRef arg_producer() {
HasConstRef result = { "there be dragons" };
return result; // UB
}
void f_call() {
f(arg_producer()); // *boom*, no diagnostic required
}
While I totally agree with the current answer that a const-ref-membered struct can be used correctly, it is also incredibly easy to use incorrectly without any help from the compiler. I would rather not do this.
I find that "hard to use incorrectly" is quite a long shot from "impossible to use incorrectly". And "normal" const-ref parameters, just like normal data members are hard-to-use-incorrectly (as far as C++ goes). const& members on the other hand are easy-to-use-incorrectly. Others seem to disagree: See the answer below. Alternatives still welcome.
HasConstRefor use pointers. (Btw.,"there be dragons"won't disappear when you exitarg_producer(), it exists till static destruction at minimum, but I can guess what you were thinking about - and even then, that's an issue ofarg_producer).