问题
I want to omit some template parameter T1,T2 when create an instance of a class DeriveGenerator<T3,T4,T1,T2> to comfort my life.
Here is the ultimately simplified version of what I am encountering.
My library:-
The important part is the class declaration. (this line)
Their internal content is just a filler.
template<class T1,class T2>class BaseGenerator{ //<-- this line
public: std::pair<T1*,T2*> generateBase(){
/** actually create T3,T4 internally */
return std::pair<T1*,T2*>(nullptr,nullptr);
}
};
template<class T3,class T4,class T1,class T2>class DeriveGenerator{ //<-- this line
public: Base<T1,T2>* base;
public: std::pair<T3*,T4*> generateDerive(){
auto pp=base->generateBase();
return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
}
};
User:-
class B1{};class B2{};
class B3:public B1{};
class B4:public B2{};
int main() {
//v this is what I have to
BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4,B1,B2> deriveGen; //so dirty #1
deriveGen.base=&baseGen;
deriveGen.generateDerive();
}
Question
Is it possible to make the line #1 cleaner?
I want the type of deriveGen depends on the type of baseGen.
Here is what I wish for :-
BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4> deriveGen; //<-- modified
deriveGen.base=&baseGen;
or at least something like:-
BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4, DECLARATION_TYPE(baseGen) > deriveGen; //<-- modified
deriveGen.base=&baseGen;
I have read (still no clue):-
- Omitting arguments in C++ Templates
- Skipping a C++ template parameter
- Difference when omitting the C++ template argument list
I don't even know if it is possible.
"decltype" seems to be the closest clue, but I can't find a way to apply it to this case.
I think I may have to split it to T1,T2.... (?)
Edit
In real case, baseGen is a non-static field of some classes that is not instantiated yet e.g.
class Holder{
public: BaseGenerator<B1,B2> baseGen;
};
Therefore, at the time of declaring deriveGen, I can't reach the real instance of baseGen.
That is the hard part.
I can refer baseGen's type via decltype, though.
(sorry for not mention about it)
回答1:
Not sure to understand what you want but... I suppose you can define DeriveGenerator in this way
template <typename, typename, typename>
class DeriveGenerator;
template <typename T3, typename T4, typename T1, typename T2>
class DeriveGenerator<T3, T4, BaseGenerator<T1, T2>>
{
public:
BaseGenerator<T1,T2>* base;
public:
std::pair<T3*,T4*> generateDerive ()
{
auto pp=base->generateBase();
return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
}
};
and use it as follows
BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4,decltype(baseGen)> deriveGen;
This if you're interested in T1 and T2 types; if you're only interested in BaseGenerator<T1, T2> you can simply write
template <typename T3, typename T4, typename Tbase>
class DeriveGenerator
{
public:
Tbase * base;
public:
std::pair<T3*,T4*> generateDerive ()
{
auto pp=base->generateBase();
return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
}
};
回答2:
If you can change DeriveGenerator definition to:
template<class T3, class T4, class BaseT>
class DeriveGenerator{
public:
BaseT* base = nullptr;
std::pair<T3*, T4*> generateDerive(){
auto pp = base->generateBase();
return {(T3*)(pp.first), (T4*)(pp.second)};
}
};
You might use:
BaseGenerator<B1, B2> baseGen;
DeriveGenerator<B3, B4, decltype(baseGen)> deriveGen;
Else I suggest to create an helper:
template <typename B3, typename B4, typename B1, typename B2>
DeriveGenerator<B3, B4, B1, B2>
MakeDerived(BaseGenerator<B1, B2>& baseGen)
{
DeriveGenerator<B3, B4, B1, B2> deriveGen;
deriveGen.base = &baseGen;
return deriveGen;
}
and then use
BaseGenerator<B1,B2> baseGen;
auto deriveGen = MakeDerived<B3, B4>(baseGen);
回答3:
I think it's simpler to push the T1 and T2 template parameters for DeriveGenerator into the actual generateDerive method itself:
template<class T3,class T4>
class DeriveGenerator{
public:
template<class T1, class T2, template<class, class> class Base>
std::pair<T3*,T4*> generateDerive(Base<T1, T2>* base){
static_assert(std::is_base_of<T1, T3>::value && std::is_base_of<T2, T4>::value, "T1 should be base of T3 and T2 should be base of T4");
auto pp=base->generateBase();
return std::pair<T3*,T4*>((T3*)(pp.first),(T4*)(pp.second));
}
};
Demo
Now you can call it like so:
BaseGenerator<B1,B2> baseGen;
DeriveGenerator<B3,B4> deriveGen;
deriveGen.generateDerive(&baseGen);
I added a static_assert to DeriveGenerator::generateDerive that ensured proper base/derived pairing using std::is_base_of so that you'll get a compiler error if you accidentally mess up the template parameters.
class B5{}; // new class B5 nobody inherits from
BaseGenerator<B1, B5> bad_baseGen;
deriveGen.generateDerive(&bad_baseGen);
来源:https://stackoverflow.com/questions/43517158/omit-template-arguments-when-create-an-instance-of-template-class-from-another-i