Avoid accumulation of iterator types in template arguments
Following principle: when I want to return a collection from a function I will pass an output iterator and let the caller decide where the output should go.
Consider the class which has n
methods and each one returns some collection. This mean that I need to construct class with n
template parameters (output iterators). The number of template parameters will start to grow, and I don't know how to handle this problem.
Specific example:
template<class TNode, class TEdge> class AGraph;
template<class TNode, class TEdge, class OutputOfFunc1, class OutputOfFunc2>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances) = 0;//func2
};
And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation
. But the problem is that I introduce template arguments
...class OutputOfFunc1, class OutputOfFunc2>
which I fell that they should not be in the class definition since they are specific to particular function.
Currently I declare the class like this
// Example of declaration
APathCalculation<
int, // type of node
double, // type of edge
back_insert_iterator<list<size_t>>, // return type of shortest path between two nodes
back_insert_iterator<vector<double>> // return type of shortest distances from source node
> &pathCalculator;
c++ templates iterator
|
show 5 more comments
Following principle: when I want to return a collection from a function I will pass an output iterator and let the caller decide where the output should go.
Consider the class which has n
methods and each one returns some collection. This mean that I need to construct class with n
template parameters (output iterators). The number of template parameters will start to grow, and I don't know how to handle this problem.
Specific example:
template<class TNode, class TEdge> class AGraph;
template<class TNode, class TEdge, class OutputOfFunc1, class OutputOfFunc2>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances) = 0;//func2
};
And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation
. But the problem is that I introduce template arguments
...class OutputOfFunc1, class OutputOfFunc2>
which I fell that they should not be in the class definition since they are specific to particular function.
Currently I declare the class like this
// Example of declaration
APathCalculation<
int, // type of node
double, // type of edge
back_insert_iterator<list<size_t>>, // return type of shortest path between two nodes
back_insert_iterator<vector<double>> // return type of shortest distances from source node
> &pathCalculator;
c++ templates iterator
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
1
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
1
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived fromAPathCalculation<int,double,some_iterator_type_A>
and another deriving fromAPathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?
– user463035818
Nov 22 '18 at 13:59
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to putvector
in abstract class declaration (why function wouldn't be able to return list for example).
– Dejan
Nov 22 '18 at 14:07
1
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10
|
show 5 more comments
Following principle: when I want to return a collection from a function I will pass an output iterator and let the caller decide where the output should go.
Consider the class which has n
methods and each one returns some collection. This mean that I need to construct class with n
template parameters (output iterators). The number of template parameters will start to grow, and I don't know how to handle this problem.
Specific example:
template<class TNode, class TEdge> class AGraph;
template<class TNode, class TEdge, class OutputOfFunc1, class OutputOfFunc2>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances) = 0;//func2
};
And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation
. But the problem is that I introduce template arguments
...class OutputOfFunc1, class OutputOfFunc2>
which I fell that they should not be in the class definition since they are specific to particular function.
Currently I declare the class like this
// Example of declaration
APathCalculation<
int, // type of node
double, // type of edge
back_insert_iterator<list<size_t>>, // return type of shortest path between two nodes
back_insert_iterator<vector<double>> // return type of shortest distances from source node
> &pathCalculator;
c++ templates iterator
Following principle: when I want to return a collection from a function I will pass an output iterator and let the caller decide where the output should go.
Consider the class which has n
methods and each one returns some collection. This mean that I need to construct class with n
template parameters (output iterators). The number of template parameters will start to grow, and I don't know how to handle this problem.
Specific example:
template<class TNode, class TEdge> class AGraph;
template<class TNode, class TEdge, class OutputOfFunc1, class OutputOfFunc2>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances) = 0;//func2
};
And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation
. But the problem is that I introduce template arguments
...class OutputOfFunc1, class OutputOfFunc2>
which I fell that they should not be in the class definition since they are specific to particular function.
Currently I declare the class like this
// Example of declaration
APathCalculation<
int, // type of node
double, // type of edge
back_insert_iterator<list<size_t>>, // return type of shortest path between two nodes
back_insert_iterator<vector<double>> // return type of shortest distances from source node
> &pathCalculator;
c++ templates iterator
c++ templates iterator
asked Nov 22 '18 at 13:52
DejanDejan
333314
333314
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
1
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
1
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived fromAPathCalculation<int,double,some_iterator_type_A>
and another deriving fromAPathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?
– user463035818
Nov 22 '18 at 13:59
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to putvector
in abstract class declaration (why function wouldn't be able to return list for example).
– Dejan
Nov 22 '18 at 14:07
1
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10
|
show 5 more comments
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
1
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
1
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived fromAPathCalculation<int,double,some_iterator_type_A>
and another deriving fromAPathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?
– user463035818
Nov 22 '18 at 13:59
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to putvector
in abstract class declaration (why function wouldn't be able to return list for example).
– Dejan
Nov 22 '18 at 14:07
1
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
1
1
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
1
1
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived from
APathCalculation<int,double,some_iterator_type_A>
and another deriving from APathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?– user463035818
Nov 22 '18 at 13:59
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived from
APathCalculation<int,double,some_iterator_type_A>
and another deriving from APathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?– user463035818
Nov 22 '18 at 13:59
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.
vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to put vector
in abstract class declaration (why function wouldn't be able to return list for example).– Dejan
Nov 22 '18 at 14:07
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.
vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to put vector
in abstract class declaration (why function wouldn't be able to return list for example).– Dejan
Nov 22 '18 at 14:07
1
1
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10
|
show 5 more comments
4 Answers
4
active
oldest
votes
Here are three alternatives to consider:
Option 1: Just pick a type and return it
If you're worried about performance, this likely isn't nearly as bad as you think.
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
Option 2: Accept a callback
The idea is that the caller will supply a lambda that stores the output in whatever way makes sense.
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
Option 3: Use a function template
It's a little mysterious to me why you want to use polymorphism for this. You could write function templates for different shortest path algorithms (similar to the algorithms style in the STL):
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
There are of course lots of variations you could do on these approaches. I would also caution against the use of output iterators like this. The caller could pass something safe like a back_inserter
but could also pass in something dangerous like a raw pointer, which could easily lead to a buffer overrun.
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
add a comment |
"Consider the class which has n methods and each one returns some
collection. This mean that I need to construct class with n template
parameters (output iterators).
No, you don't. You coudd in fact create a class with 0 template parameters. However, each method itself has one template parameter. In your case, you can reduce it to just 2 template parameters for the class:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
Please note that I have made one important change here: this is a class in the OO sense. What you had was an abstract class or interface. Abstract classes are a good way to decouple caller and callee, but here you cannot decouple them: the caller and callee must agree on iterator types.
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on othern-1
iterators which he doesn't use. This part is bad.
– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller ofAPathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specifyOutputOfFunc2
. I indeed removed polymorpism, intentionally.
– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your codeAPathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic
– user463035818
Nov 22 '18 at 14:37
|
show 6 more comments
You may define all your arguments in a single structure and use them all over your structures like this:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
All that will not change anything of your binary. It only makes it a bit more convenient. But maybe I have misunderstood your question?
add a comment |
A "shallow" answer:
In order to avoid N template parameters in one class, you need to split it into N classes with 1 template parameter each.
A "deep" answer:
You cannot easily combine dynamic (runtime) polymorphism of virtual functions with static (compile-time, template based) polymorphism of its argument types. If users of the APathCalculation
interface want to provide their own iterator class, they will need to instantiate all the potentially useful actual implementations of this interface for their iterator class, which makes dynamic polymorphism for APathCalculation
a redundant idea.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
You can actually have both (statically polymorphous template specializations for common algorithms and/or common iterators, backed by a "default" case that is a wrapper to dynamically polymorphous implementations), but that's probably too complicated for your task. Besides, you will still need a way to bind your "default" wrapper to the actual algorithm you want the APathCalculation
client to call.
I personally would start with a purely template-based solution, but I could understand someone starting with a purely virtual function based solution.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?
– Dejan
Nov 22 '18 at 16:27
It could be a functor (eitherstd::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfiesOutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at thestd::back_insert_iterator
implementation in order to see which methods you need to implement.
– Kit.
Nov 22 '18 at 17:04
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53432489%2favoid-accumulation-of-iterator-types-in-template-arguments%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
4 Answers
4
active
oldest
votes
4 Answers
4
active
oldest
votes
active
oldest
votes
active
oldest
votes
Here are three alternatives to consider:
Option 1: Just pick a type and return it
If you're worried about performance, this likely isn't nearly as bad as you think.
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
Option 2: Accept a callback
The idea is that the caller will supply a lambda that stores the output in whatever way makes sense.
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
Option 3: Use a function template
It's a little mysterious to me why you want to use polymorphism for this. You could write function templates for different shortest path algorithms (similar to the algorithms style in the STL):
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
There are of course lots of variations you could do on these approaches. I would also caution against the use of output iterators like this. The caller could pass something safe like a back_inserter
but could also pass in something dangerous like a raw pointer, which could easily lead to a buffer overrun.
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
add a comment |
Here are three alternatives to consider:
Option 1: Just pick a type and return it
If you're worried about performance, this likely isn't nearly as bad as you think.
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
Option 2: Accept a callback
The idea is that the caller will supply a lambda that stores the output in whatever way makes sense.
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
Option 3: Use a function template
It's a little mysterious to me why you want to use polymorphism for this. You could write function templates for different shortest path algorithms (similar to the algorithms style in the STL):
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
There are of course lots of variations you could do on these approaches. I would also caution against the use of output iterators like this. The caller could pass something safe like a back_inserter
but could also pass in something dangerous like a raw pointer, which could easily lead to a buffer overrun.
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
add a comment |
Here are three alternatives to consider:
Option 1: Just pick a type and return it
If you're worried about performance, this likely isn't nearly as bad as you think.
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
Option 2: Accept a callback
The idea is that the caller will supply a lambda that stores the output in whatever way makes sense.
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
Option 3: Use a function template
It's a little mysterious to me why you want to use polymorphism for this. You could write function templates for different shortest path algorithms (similar to the algorithms style in the STL):
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
There are of course lots of variations you could do on these approaches. I would also caution against the use of output iterators like this. The caller could pass something safe like a back_inserter
but could also pass in something dangerous like a raw pointer, which could easily lead to a buffer overrun.
Here are three alternatives to consider:
Option 1: Just pick a type and return it
If you're worried about performance, this likely isn't nearly as bad as you think.
virtual std::vector<TEdge> FindShortestPath(size_t source, size_t dest, TGraph& graph) = 0;
Option 2: Accept a callback
The idea is that the caller will supply a lambda that stores the output in whatever way makes sense.
virtual void TraverseShortestPath(
size_t source,
size_t dest,
TGraph& graph,
std::function<void(TEdge*)> callback) = 0;
Option 3: Use a function template
It's a little mysterious to me why you want to use polymorphism for this. You could write function templates for different shortest path algorithms (similar to the algorithms style in the STL):
template <class TGraph, class OutIt>
void FindShortestPath(size_t source, size_t dest, TGraph& graph, OutIt output)
{
// details...
}
There are of course lots of variations you could do on these approaches. I would also caution against the use of output iterators like this. The caller could pass something safe like a back_inserter
but could also pass in something dangerous like a raw pointer, which could easily lead to a buffer overrun.
edited Nov 22 '18 at 14:50
answered Nov 22 '18 at 14:40
Peter RudermanPeter Ruderman
10.2k2352
10.2k2352
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
add a comment |
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
In option 3: I am loosing polymorphism. Option 1 is not general since I am specifying the return type. But option 2 sounds interesting, something that it doesn't came to my mind. Maybe, I should think to write library in that way.
– Dejan
Nov 22 '18 at 15:01
1
1
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
Option (3) looses runtime polymorphism, but why do you need it? Do you really expect users of your library to choose different shortest path algorithms at runtime?
– Peter Ruderman
Nov 22 '18 at 15:03
add a comment |
"Consider the class which has n methods and each one returns some
collection. This mean that I need to construct class with n template
parameters (output iterators).
No, you don't. You coudd in fact create a class with 0 template parameters. However, each method itself has one template parameter. In your case, you can reduce it to just 2 template parameters for the class:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
Please note that I have made one important change here: this is a class in the OO sense. What you had was an abstract class or interface. Abstract classes are a good way to decouple caller and callee, but here you cannot decouple them: the caller and callee must agree on iterator types.
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on othern-1
iterators which he doesn't use. This part is bad.
– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller ofAPathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specifyOutputOfFunc2
. I indeed removed polymorpism, intentionally.
– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your codeAPathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic
– user463035818
Nov 22 '18 at 14:37
|
show 6 more comments
"Consider the class which has n methods and each one returns some
collection. This mean that I need to construct class with n template
parameters (output iterators).
No, you don't. You coudd in fact create a class with 0 template parameters. However, each method itself has one template parameter. In your case, you can reduce it to just 2 template parameters for the class:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
Please note that I have made one important change here: this is a class in the OO sense. What you had was an abstract class or interface. Abstract classes are a good way to decouple caller and callee, but here you cannot decouple them: the caller and callee must agree on iterator types.
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on othern-1
iterators which he doesn't use. This part is bad.
– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller ofAPathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specifyOutputOfFunc2
. I indeed removed polymorpism, intentionally.
– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your codeAPathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic
– user463035818
Nov 22 '18 at 14:37
|
show 6 more comments
"Consider the class which has n methods and each one returns some
collection. This mean that I need to construct class with n template
parameters (output iterators).
No, you don't. You coudd in fact create a class with 0 template parameters. However, each method itself has one template parameter. In your case, you can reduce it to just 2 template parameters for the class:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
Please note that I have made one important change here: this is a class in the OO sense. What you had was an abstract class or interface. Abstract classes are a good way to decouple caller and callee, but here you cannot decouple them: the caller and callee must agree on iterator types.
"Consider the class which has n methods and each one returns some
collection. This mean that I need to construct class with n template
parameters (output iterators).
No, you don't. You coudd in fact create a class with 0 template parameters. However, each method itself has one template parameter. In your case, you can reduce it to just 2 template parameters for the class:
template<class TNode, class TEdge>
class APathCalculation
{
using TGraph = AGraph<TNode, TEdge>;
public:
template<class OutputOfFunc1>
void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, OutputOfFunc1 outPath);
template<class OutputOfFunc2>
void ReturnAllShortestDistances(size_t source, TGraph& graph, OutputOfFunc2 outDistances);
};
Please note that I have made one important change here: this is a class in the OO sense. What you had was an abstract class or interface. Abstract classes are a good way to decouple caller and callee, but here you cannot decouple them: the caller and callee must agree on iterator types.
answered Nov 22 '18 at 14:23
MSaltersMSalters
134k8117268
134k8117268
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on othern-1
iterators which he doesn't use. This part is bad.
– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller ofAPathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specifyOutputOfFunc2
. I indeed removed polymorpism, intentionally.
– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your codeAPathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic
– user463035818
Nov 22 '18 at 14:37
|
show 6 more comments
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on othern-1
iterators which he doesn't use. This part is bad.
– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller ofAPathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specifyOutputOfFunc2
. I indeed removed polymorpism, intentionally.
– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your codeAPathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic
– user463035818
Nov 22 '18 at 14:37
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on other
n-1
iterators which he doesn't use. This part is bad.– Dejan
Nov 22 '18 at 14:29
You lost virtual, polymorphism and decoupling. If the caller calls only one function, he must agree on output iterator types. That is fine. But that caller function also need to agree on other
n-1
iterators which he doesn't use. This part is bad.– Dejan
Nov 22 '18 at 14:29
@Dejan: No, they don't. Not in this solution. The caller of
APathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specify OutputOfFunc2
. I indeed removed polymorpism, intentionally.– MSalters
Nov 22 '18 at 14:30
@Dejan: No, they don't. Not in this solution. The caller of
APathCalculation<TNode, TEdge>::ReturnShortestPath<OutputOfFunc1>
clearly does not need to specify OutputOfFunc2
. I indeed removed polymorpism, intentionally.– MSalters
Nov 22 '18 at 14:30
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
Yes in that case. But you lost polymorphism then. Having abstract class for path calculation makes sense to me. Will you design your library without such abstract class?
– Dejan
Nov 22 '18 at 14:34
1
1
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
@Dejan: Yes. That is the modern C++ style. All those iterators do not share an abstract base class either.
– MSalters
Nov 22 '18 at 14:36
1
1
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your code
APathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic– user463035818
Nov 22 '18 at 14:37
@Dejan its not clear how you intend to use the base class. Note that already "And, I will derive different classes (e.g. Dijkstra, Bellman-Ford) from APathCalculation." is strictly speaking wrong. In your code
APathCalculation
is not a class, its just a template, as such you cannot derive from it. You first have to instantiate it. Classes deriving from two different instantations of the same template are not polymorphic– user463035818
Nov 22 '18 at 14:37
|
show 6 more comments
You may define all your arguments in a single structure and use them all over your structures like this:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
All that will not change anything of your binary. It only makes it a bit more convenient. But maybe I have misunderstood your question?
add a comment |
You may define all your arguments in a single structure and use them all over your structures like this:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
All that will not change anything of your binary. It only makes it a bit more convenient. But maybe I have misunderstood your question?
add a comment |
You may define all your arguments in a single structure and use them all over your structures like this:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
All that will not change anything of your binary. It only makes it a bit more convenient. But maybe I have misunderstood your question?
You may define all your arguments in a single structure and use them all over your structures like this:
template<class TNode, class TEdge> class AGraph;
template< typename TYPE_DEFS >
class APathCalculation
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) = 0;//func1
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) = 0;//func2
};
template< typename TYPE_DEFS >
class Dijkstra: public APathCalculation<TYPE_DEFS>
{
using TGraph = AGraph<typename TYPE_DEFS::TNode, typename TYPE_DEFS::TEdge>;
public:
virtual void ReturnShortestPath(size_t source, size_t dest, TGraph& graph, typename TYPE_DEFS::OutputOfFunc1 outPath) override {}
virtual void ReturnAllShortestDistances(size_t source, TGraph& graph, typename TYPE_DEFS::OutputOfFunc2 outDistances) override {}
};
struct TypeDefs
{
using TNode = int;
using TEdge = double;
using OutputOfFunc1 = std::back_insert_iterator<std::list<size_t>>;
using OutputOfFunc2 = std::back_insert_iterator<std::vector<double>>;
};
int main()
{
Dijkstra<TypeDefs> d;
}
All that will not change anything of your binary. It only makes it a bit more convenient. But maybe I have misunderstood your question?
answered Nov 22 '18 at 15:17
KlausKlaus
10.9k12559
10.9k12559
add a comment |
add a comment |
A "shallow" answer:
In order to avoid N template parameters in one class, you need to split it into N classes with 1 template parameter each.
A "deep" answer:
You cannot easily combine dynamic (runtime) polymorphism of virtual functions with static (compile-time, template based) polymorphism of its argument types. If users of the APathCalculation
interface want to provide their own iterator class, they will need to instantiate all the potentially useful actual implementations of this interface for their iterator class, which makes dynamic polymorphism for APathCalculation
a redundant idea.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
You can actually have both (statically polymorphous template specializations for common algorithms and/or common iterators, backed by a "default" case that is a wrapper to dynamically polymorphous implementations), but that's probably too complicated for your task. Besides, you will still need a way to bind your "default" wrapper to the actual algorithm you want the APathCalculation
client to call.
I personally would start with a purely template-based solution, but I could understand someone starting with a purely virtual function based solution.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?
– Dejan
Nov 22 '18 at 16:27
It could be a functor (eitherstd::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfiesOutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at thestd::back_insert_iterator
implementation in order to see which methods you need to implement.
– Kit.
Nov 22 '18 at 17:04
add a comment |
A "shallow" answer:
In order to avoid N template parameters in one class, you need to split it into N classes with 1 template parameter each.
A "deep" answer:
You cannot easily combine dynamic (runtime) polymorphism of virtual functions with static (compile-time, template based) polymorphism of its argument types. If users of the APathCalculation
interface want to provide their own iterator class, they will need to instantiate all the potentially useful actual implementations of this interface for their iterator class, which makes dynamic polymorphism for APathCalculation
a redundant idea.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
You can actually have both (statically polymorphous template specializations for common algorithms and/or common iterators, backed by a "default" case that is a wrapper to dynamically polymorphous implementations), but that's probably too complicated for your task. Besides, you will still need a way to bind your "default" wrapper to the actual algorithm you want the APathCalculation
client to call.
I personally would start with a purely template-based solution, but I could understand someone starting with a purely virtual function based solution.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?
– Dejan
Nov 22 '18 at 16:27
It could be a functor (eitherstd::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfiesOutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at thestd::back_insert_iterator
implementation in order to see which methods you need to implement.
– Kit.
Nov 22 '18 at 17:04
add a comment |
A "shallow" answer:
In order to avoid N template parameters in one class, you need to split it into N classes with 1 template parameter each.
A "deep" answer:
You cannot easily combine dynamic (runtime) polymorphism of virtual functions with static (compile-time, template based) polymorphism of its argument types. If users of the APathCalculation
interface want to provide their own iterator class, they will need to instantiate all the potentially useful actual implementations of this interface for their iterator class, which makes dynamic polymorphism for APathCalculation
a redundant idea.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
You can actually have both (statically polymorphous template specializations for common algorithms and/or common iterators, backed by a "default" case that is a wrapper to dynamically polymorphous implementations), but that's probably too complicated for your task. Besides, you will still need a way to bind your "default" wrapper to the actual algorithm you want the APathCalculation
client to call.
I personally would start with a purely template-based solution, but I could understand someone starting with a purely virtual function based solution.
A "shallow" answer:
In order to avoid N template parameters in one class, you need to split it into N classes with 1 template parameter each.
A "deep" answer:
You cannot easily combine dynamic (runtime) polymorphism of virtual functions with static (compile-time, template based) polymorphism of its argument types. If users of the APathCalculation
interface want to provide their own iterator class, they will need to instantiate all the potentially useful actual implementations of this interface for their iterator class, which makes dynamic polymorphism for APathCalculation
a redundant idea.
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
You can actually have both (statically polymorphous template specializations for common algorithms and/or common iterators, backed by a "default" case that is a wrapper to dynamically polymorphous implementations), but that's probably too complicated for your task. Besides, you will still need a way to bind your "default" wrapper to the actual algorithm you want the APathCalculation
client to call.
I personally would start with a purely template-based solution, but I could understand someone starting with a purely virtual function based solution.
answered Nov 22 '18 at 16:18
Kit.Kit.
52549
52549
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?
– Dejan
Nov 22 '18 at 16:27
It could be a functor (eitherstd::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfiesOutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at thestd::back_insert_iterator
implementation in order to see which methods you need to implement.
– Kit.
Nov 22 '18 at 17:04
add a comment |
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?
– Dejan
Nov 22 '18 at 16:27
It could be a functor (eitherstd::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfiesOutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at thestd::back_insert_iterator
implementation in order to see which methods you need to implement.
– Kit.
Nov 22 '18 at 17:04
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?– Dejan
Nov 22 '18 at 16:27
If you really need dynamic polymorphism, you need a dynamically polymorphous iterator class.
I don't understand this. Can you explain what you mean with this? How the signature in abstract class will look like in that case?– Dejan
Nov 22 '18 at 16:27
It could be a functor (either
std::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfies OutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at the std::back_insert_iterator
implementation in order to see which methods you need to implement.– Kit.
Nov 22 '18 at 17:04
It could be a functor (either
std::function
or a class with a virtual function interface), or, if you want to use in place of "normal" output iterators, it could be a wrapper class that satisfies OutputIterator
requirements and redirects the required operator calls to the underlying polymorphous class (which it may hold by reference). You can look at the std::back_insert_iterator
implementation in order to see which methods you need to implement.– Kit.
Nov 22 '18 at 17:04
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53432489%2favoid-accumulation-of-iterator-types-in-template-arguments%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
"I fell that they should not be in the class definition since they are specific to particular function" you know that you can make the individual methods templated?
– user463035818
Nov 22 '18 at 13:54
1
@user463035818 I cannot make it template virtual. Implementation of path calculation is in derived class.
– Dejan
Nov 22 '18 at 13:55
1
i am not sure if I understand your code, hence let me just ask another question. You know that Two classes, one derived from
APathCalculation<int,double,some_iterator_type_A>
and another deriving fromAPathCalculation<int,double,some_iterator_type_B>
, are completely unrelated (ie they do not share a common base class) ?– user463035818
Nov 22 '18 at 13:59
@user463035818 Let's say that I want to make different implementaion of shortest path (in derived classes). If base class function return e.g.
vector<TEdge>
then I do not have this problem. But I want more general solution, I do not want to putvector
in abstract class declaration (why function wouldn't be able to return list for example).– Dejan
Nov 22 '18 at 14:07
1
@Dejan His question was not about that. The question is "why do you implement the algorithms in derived classes of that base class? Why use polymorphy?". If the answer to that is "because I want to store different instances of that algorithm through the base class interface", then his point is "you can't do that if the base classes are different template instantiations".
– Max Langhof
Nov 22 '18 at 14:10