-1

I have a problem where I want to overload 2 functions by swapping the default parameters to make it possible to swap them in a call.

Create(UncachedGraph* graph, std::string name, Node* parent, std::string path = "", int pos = -1);
Create(UncachedGraph* graph, std::string name, Node* parent, int pos = -1, std::string path = "");

but the problem is that if you call it without passing any arguments to default parameters, the call is ambiguous

Create(this->graph, this->node->getName(), this->node->getParent()); // Create(graph, name, parent) is ambiguous since the overload to be called cannot be resolved

and I understand why it is like that. But I would really love to somehow resolve it by prioritizing one overload over another by some rule, like for example with some "magic qualifier"

prioritized Create(UncachedGraph* graph, std::string name, Node* parent, std::string path = "", int pos = -1);
Create(UncachedGraph* graph, std::string name, Node* parent, int pos = -1, std::string path = "");

which in the above call would resolve the ambiguity by calling the first overload, or something else. The thing is, in this case I simply do not care which of the overloads is being called, because there they are just the same.

So, is there any possible thing I can do to resolve this problem?

NotYourFox
  • 35
  • 5
  • 2
    Why you are doing it like that? This API is quite confusing. I would recommend to just remove one of the functions. – Angelicos Phosphoros May 21 '23 at 12:09
  • Maybe [How to use static_cast to resolve overloaded functions?](https://stackoverflow.com/questions/14279874/how-to-use-static-cast-to-resolve-overloaded-functions) – Jason May 21 '23 at 12:15
  • If you want to make the API even more confusing, you *could* make one of the functions a template (with a default template parameter) `template`. Templates are only chosen if they are a better match than a non-template. – BoP May 21 '23 at 12:20
  • 2
    Why do you have these standalone create functions? And not a Graph object where you have an add method for a node returning a reference to a node? I think your design is leaking too many details. e.g. as a client be able to type `Node& Graph::Create(NodeData&& data)` and `Edge& Graph::Create(Node& from, Node& to, EdgeData&& data)` (where NodeData is a struct with name, and other data fields) – Pepijn Kramer May 21 '23 at 12:21
  • Why would you even want the overloads? How are you expecting to use the second overload without supplying `pos`? – Ted Lyngmo May 21 '23 at 12:26
  • I'd also recommend the Builder Pattern: https://en.wikipedia.org/wiki/Builder_pattern. – Ulrich Eckhardt May 21 '23 at 12:27
  • @PepijnKramer the reason behind those are undo/redos which I wanted to implement without having to use a ton of if statements for each event. – NotYourFox May 21 '23 at 12:31
  • Even if there was a way to qualify it, using `prioritized` as you suggest, the overload with `pos` first can't be used without actually supplying `pos` - so the default `-1` can be removed and then the abiguity is gone. – Ted Lyngmo May 21 '23 at 12:32
  • That is not reason enough, that could be solved by adding an undo and redo stack within the graph class itself and exposing Undo and Redo methods on the graph class. Key of OO design is encapsulation, in other words hide implementation using operations. Or if you want Undo/Redo outside of the class then you can have a stack of method calls (have a look at std::queue/std::function and lambda expressions). – Pepijn Kramer May 21 '23 at 12:33
  • @PepijnKramer if you do an undo/redo stack and a function for it, you would need to have a bunch of branching to tell how to undo the thing for every command. That is what I'm trying to avoid. – NotYourFox May 21 '23 at 12:40
  • In C++ you can use the standard library and you can write code like `undo_stack.push([&]{ graph.remove(node); }` with undo_stack being a std::stack>. The argument to push is a [lambda expression](https://en.cppreference.com/w/cpp/language/lambda). So basically for each action you can add an inverse action to the stack. If you put this inside your class, the Create method will not only create a node but also push the corresponding undo action onto the undo stack. – Pepijn Kramer May 21 '23 at 12:52
  • @PepijnKramer Just wanted to clarify, in my code with those separate functions (which are actually constructors of classes derived from abstract class Command with methods execute() and undo()) there are raw methods in UncachedGraph which are being called inside Command.execute() and standard methods in Graph which call those commands and conveniently append them to cache of type std::stack. So they serve as a temporary dataclasses and make the further implementation easier because the only thing you need to do is to call undo_stack.pop().undo() and redo_stack.pop().execute() – NotYourFox May 21 '23 at 15:44

1 Answers1

0

but the problem is that if you call it without passing any arguments to default parameters, the call is ambiguous

Well, to resolve ambiguity at least one of the parameters in one of the function declarations must be defined without default:

Create(UncachedGraph* graph, std::string name, Node* parent,  
       std::string path = "", int pos);
Create(UncachedGraph* graph, std::string name, Node* parent,  
       int pos = -1, std::string path = "");

The thing is, in this case I simply do not care which of the overloads is being called, because there they are just the same.

You can simply implement the other function with all defaults to call the other one, using parameters in right order:

Create(UncachedGraph* graph, std::string name, Node* parent,  
       int pos = -1, std::string path = "") {
    Create(graph, name, parent, path, pos);
}
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • 1
    In your first code block a positional argument follows a default one, which is not allowed in C++. I understood your solution and it helped me, just pointing out the typo – NotYourFox May 21 '23 at 12:46