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 filter
and 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.