More elegant way to make a C++ member function change different member variables based on template parameter?
Today, I wrote some code that needed to add elements to different container variables depending on the type of a template parameter. I solved it by writing a friend helper class specialized on its own template parameter which had a member variable of the original class. It saved me a few hundred lines of repeating myself without adding much complexity. However, it seemed kludgey. I would like to know if there is a better, more elegant way.
The code below is a greatly simplified example illustrating the problem and my solution. It compiles in g++.
#include <vector>
#include <algorithm>
#include <iostream>
namespace myNS{
template<class Elt>
struct Container{
std::vector<Elt> contents;
template<class Iter>
void set(Iter begin, Iter end){
contents.erase(contents.begin(), contents.end());
std::copy(begin, end, back_inserter(contents));
}
};
struct User;
namespace WkNS{
template<class Elt>
struct Worker{
User& u;
Worker(User& u):u(u){}
template<class Iter>
void set(Iter begin, Iter end);
};
};
struct F{ int x; explicit F(int x):x(x){} };
struct G{ double x; explicit G(double x):x(x){} };
struct User{
Container<F> a;
Container<G> b;
template<class Elt>
void doIt(Elt x, Elt y){
std::vector<Elt> v; v.push_back(x); v.push_back(y);
Worker<Elt>(*this).set(v.begin(), v.end());
}
};
namespace WkNS{
template<class Elt> template<class Iter>
void Worker<Elt>::set(Iter begin, Iter end){
std::cout << "Set a." << std::endl;
u.a.set(begin, end);
}
template<> template<class Iter>
void Worker<G>::s开发者_运维问答et(Iter begin, Iter end){
std::cout << "Set b." << std::endl;
u.b.set(begin, end);
}
};
};
int main(){
using myNS::F; using myNS::G;
myNS::User u;
u.doIt(F(1),F(2));
u.doIt(G(3),G(4));
}
User
is the class I was writing.
Worker
is my helper class. I have it in its own namespace because I don't want it causing trouble outside myNS.
Container
is a container class whose definition I don't want to modify, but is used by User
in its instance variables.
doIt<F>
should modify a. doIt<G>
should modify b.
F
and G
are open to limited modification if that would produce a more elegant solution. (As an example of one such modification, in the real application F
's constructor takes a dummy parameter to make it look like G
's constructor and save me from repeating myself.)
In the real code, Worker
is a friend of User
and member variables are private. To make the example simpler to write, I made everything public. However, a solution that requires things to be public really doesn't answer my question.
Given all these caveats, is there a better way to write User::doIt
?
After reading Emile Cormier's comment, I thought of a way to both keep from repeating myself and to also eliminate the Worker
class: make two trivial non-template doIt
functions for F
and G
and have each call a third templated doIt
function with the variable to change passed as a parameter. Here is the modified code.
#include <vector>
#include <algorithm>
#include <iostream>
namespace myNS{
template<class Elt>
struct Container{
std::vector<Elt> contents;
template<class Iter>
void set(Iter begin, Iter end){
contents.erase(contents.begin(), contents.end());
std::copy(begin, end, back_inserter(contents));
}
};
struct F{ int x; explicit F(int x):x(x){} };
struct G{ double x; explicit G(double x):x(x){} };
struct User{
Container<F> a;
Container<G> b;
template<class Elt>
void doIt(Elt x, Elt y, Container<Elt>& cont, const char*name){
std::vector<Elt> v; v.push_back(x); v.push_back(y);
cont.set(v.begin(), v.end());
std::cout << "Set " << name << std::endl;
}
void doIt(F x, F y){ doIt(x,y,a,"a"); }
void doIt(G x, G y){ doIt(x,y,b,"b"); }
};
}
int main(){
using myNS::F; using myNS::G;
myNS::User u;
u.doIt(F(1),F(2));
u.doIt(G(3),G(4));
}
精彩评论