I got two classes, a B class has a member A1, and the A1 class has a reference member B, this is easy to write like this:
class B;
class A1 {
public:
A1(const B& b) : b_(b) {}
private:
const B& b_; // a reference to B
};
class B {
public:
B(A1 a1) : a1_(a1) {}
private:
A1 a1_; // a Type A1 member
};
And then I need change class B to a template class, so the code should be like this:
template<typename T1>
class B;
class A1 {
public:
A1(const B<A1> &b) : b_(b) {};
private:
const B<A1> &b_; // a reference to B<A1>
};
template<typename T1>
class B {
public:
B(T1 t1) : t1_(t1){}
private:
T1 t1_; //a T1 type member
};
And suddenly we need a second member in B, so I changed the B to this:
template<typename T1, typename T2>
class B {
public:
B(T1 t1, T2 t2) : t1_(t1), t2_(t2){}
private:
T1 t1_;
T2 t2_;
};
This got a problem:
now if I'm a coder of A, the code below is illegal:
template<typename T1, typename T2>
class B;
class A1 {
public:
A1(const B<A1> &b) : b_(b) {}; //wrong, need a second template argument
private:
const B<A1> &b_; // wrong, either
};
That means the coder must fill the second template argument which he may not know (or at least, may not care about). So I change the code to this:
template <typename T1, typename T2>
class B;
template <typename TB>
class A1 {
public:
explicit A1(const TB& b) : b_(b) {}
private:
const TB& b_;
};
template <typename TB>
class A2 {
public:
explicit A2(const TB& b) : b_(b) {}
private:
const TB& b_;
};
template <typename T1, typename T2>
class B {
public:
B(T1 a1, T2 a2) : a1_(a1), a2_(a2) {}
private:
T1 a1_;
T2 a2_;
};
This looks nice except I don't know how to make a B instance(like this:B<A1<B<A1<...>,A2>, A2>, it's recursively.)
The ideal design I'm looking forward to is that programmers for A1 and A2 simply do not need to know each other, and the programmer for B is simply add A1 and A2 more or less like this B<A1, A2>(or add some other stuff), and even if he add a T3 parameter like B<A1, A2, A3>, the code ofA1andA2` will not need to be changed.
So
1: If I insist in this syntax, does this mean I have to give up using template for B, instead use pointers?
2: If I insist in using template, does this mean the class T1 and class T2 must know each other?
3: Is there a third way by using template while T1 and T2 is independent? That is, no matter how many parameters I add to class B, the A1 class does not need to change? Template template/CRTP/SFINAE, all of this seems not useful. As the B class is only a reference in class A, the B class is more like a interface rather than a certain class, this remind me of the C++ proposal "concept", can "concept" help in this case?
4: Is this a xy problem? That means I should not design my code pattern in this way?
Here's how one might do that.
// B doesn't know about A
template <class T1, class T2>
class B {};
// A doesn't know about B
template <template<class> class TB>
class A {
using MyB = TB<A<TB>>;
};
template <class K>
using Bint = B<K, int>;
int main() {
A<Bint> abint;
}
Edit: If B has more than one template parameters, this can also be done, though a bit more complicated. One needs to tie all the template parameters in one class using some helpers. Here's an example. (I have renamed the classes for clarity).
// The top class parameterised by 3 bottom classes
template <class A1, class A2, class A3>
struct Top {};
// Three bottom classes parameterised by the top
template <template<class> class T> struct Bottom1 {};
template <template<class> class T> struct Bottom2 {};
template <template<class> class T> struct Bottom3 {};
// Until this point, none of the classes know about any other.
// Now tie them together with these helper definitions.
template <typename K>
using BundledTop = Top<typename K::A1, typename K::A2, typename K::A3>;
struct Bundle
{
using A1 = Bottom1<BundledTop>;
using A2 = Bottom2<BundledTop>;
using A3 = Bottom3<BundledTop>;
};
using MyTop = BundledTop<Bundle>;
I would say that your answer 4 is this right one: this seems as being a flawed design. I cannot prove that but I would like to give you an example.
I have implemented something similar to your code when I have been implementing bounding volume hierarchies. You could think of B as a branch class and A as a leave class of the tree (that is not completely correct, but should give an idea). Every branch has to contain its leaves, therefore B stores instances of A. But it is also nice to go back from a leave to its branch, therefore A should store a reference to B. You could decide to make your bounding volume hierarchy store arbitrary content (triangle, quads, polygons), therefore the item type should be a template parameter. Also you might decide to make the maximum leave number of each branch a template parameter of B.
But why should the branch class be able to store leaves that are made for arbitrary branches (because the B type is a template parameter of A)? If B stores instances of A, then I cannot think of any reason for making the B type a template parameter of A.
So probably you are too general here and should look for another design.
I am looking forward to your comments.
Related
Let's assume I have the following classes:
template<typename A> class Foo { ... };
template<typename A, typename B = Foo<A>> class Bar { ... };
Bar is virtual, and it can be derived with many different arguments for A and B. The template's purpose is to provide intelli-sense for the derivations. I do not want to use interfaces for A and B since they have nothing in common. Also, it would cause a lot of unnecessary casting.
The problem is that I also want to provide various algorithms that use Bar, some generic ones, and some are specialized. Something I tried looks like this:
template<typename A, typename B = Foo<A>, typename BarType = Bar<A, B>>
class Algorithm
{
void doWork(BarType& bar) { ... };
};
What I want to do is pass a derivation from Bar to the Algorithm, and it should automatically detect the arguments A and B. For example:
class BarDerivation : Bar<int, Foo<int>> { ... };
Algorithm<BarDerivation> alg;
This answer provides a solution using type-traits, the problem is that Algorithm would lose the information that BarType is from type Bar.
I'm not certain if what I'm doing is the best approach for what I want to achieve. So is there a solution that solves my problem, or are there better approaches?
Simpler would be to add alias in Foo/Bar:
template<typename A> class Foo { using type = A; };
template<typename A, typename B = Foo<A>> class Bar { using T1 = A; using T2 = B; };
class Derived : Bar<int, Foo<float>> { /*...*/ };
template <typename BarType>
class Algorithm
{
using A = typename BarType::T1;
using B = typename BarType::T2;
void doWork(BarType& bar) { ... };
};
The title is probably hard to parse so here's what I mean. Let's say I have a few classes A, B1, B2, B3... Bn where I'd like to hold a tuple of A's templated on some B class. A valid final result could be something like tuple<A<B3>, A<B1>> _chain. This would be contained within a container class we'll call C. What I'm looking for is to initialize C with a bunch of B types that then get wrapped in my A class. I can't simply do
template<class... T>
class C
{
std::tuple<T...> _chain;
public:
C(const T&... t) : _chain(t...) { }
};
because then I'd end up with a tuple of Bs. Is there an ergonomic way to wrap the init step (_chain(t...)) so that I'll end up with a tuple of A's templated on the B classes I pass to the class?
Do you mean something like this:
template<template <typename> typename Base, class... T>
class C
{
std::tuple<Base<T>...> _chain;
public:
C(const Base<T>&... t) : _chain(t...) { }
};
You can use it like this: C<A, B1, B2, B3> c;
_chain will then be a tuple tuple<A<B1>, A<B2>, A<B3>>.
Library code
My library has a CRTP class B<Derived>.
I created a Trait<T> class to enable user to change behavior of B.
The default setting is int. (#1)
#include <iostream>
#include <string>
//B and Trait (library class)
template<class Derived> class B;
template<class T>class Trait{
public: using type = int; //<-- default setting //#1
};
template<class Derived> class B{
public: using type = typename Trait<Derived>::type; //#2
public: type f(){return 1;}
};
User code ( full coliru demo )
Then, I create a new class C with a new setting std::string. (#3)
It works fine.
//C (user1's class)
template<class Derived> class C ;
template<class Derived>class Trait<C<Derived>>{
public: using type = std::string; //#3
};
template<class Derived> class C : public B<Derived>{};
Finally, I create a new class D.
I want D to derive C's setting i.e. std::string (not int).
However, it is not compilable at $.
//D (user2's class)
class D : public C<D>{ //#4
public: type f(){return "OK";} //#5
//$ invalid conversion from 'const char*' to 'B<D>::type {aka int}'
};
int main(){
D dt;
std::cout<< dt.f() <<std::endl;
}
My understanding
Roughly speaking, here is my understanding about the compile process :-
Just before class D (#4), it doesn't know about D.
At #4, to identity D::type, it looks up C<D>::type.
Finally, it finds that it is defined in B<D>::type at #2.
From #2, it travels to the definition at #1 and find type = int.
Thus D::type = int.
Note that #3 is ignored, because at this point (#4 and #5), D is still incomplete.
The compiler still doesn't fully recognize that D is derived from C<something> ... yet.
Question
How to let D automatically inherit Trait's setting from C without explicitly define another template specialization Trait<D>?
In other words, how to make #3 not ignored for D?
Trait is probably not a good design (?), but I prefer to let the type setting be in a separate trait class.
The instantiating goes like this:
D -> C<D> -> B<D> -> Traits<D>
Traits<D> does not match you partial specialization of Traits<C<Derived>>
If you change it to template<class Derived> class C : public B<C<Derived>>{}; that will in turn instantiate Traits<C<D>> and that will match your specialization and you get std::string as type.
To get the child from B you can use.
template <typename... T>
struct getChild;
template <template <typename... T> typename First, typename... Rest>
struct getChild<First<Rest...>> { using child = typename getChild<Rest...>::child; };
template <typename First>
struct getChild<First> { using child = First; };
and then add in
template<class Derived> class B{
public: using type = typename Trait<Derived>::type;
using child = typename getChild<Derived>::child;
public: type f(){return 1;}
};
I have a question about template specialization in C++, and I am hoping someone here can help. I have a class that has 3 template parameters:
template<class A, class B, class C>
class myClass {
public:
void myFunc();
};
What I want to do is write several versions of myFunc that specialize on, say, type C, but are generic for types A and B. So I do NOT want the fully templated function like this:
template<class A, class B, class C>
void myClass<A, B, C>::myFunc()
{
// function code here
}
and I do NOT want a fully specialized function like this
void myClass<int, int, int>::myFunc()
{
// code goes here
}
Instead, I want to do something that would be similar to
template<class A, class B>
void myClass<A, B, int>::myFunc()
{
// code goes here
}
The idea is that if class type C is int, I would call one version of myFunc(), and if class type C is double, I would call a different version of myFunc. I've tried lots of difference combinations of template specialization syntaxes (too many to list here), and none seems to compile.
Could someone possibly point me in the right direction here? Thanks in advance for your help.
Michael
You can write a function template, and an overload, and delegate the work to it:
template<class A, class B, class C>
class myClass
{
//resolver doesn't need to define anything in it!
template<class> struct resolver {}; //empty, yet powerful!
public:
void myFunc()
{
doFun(resolver<C>());
}
//this is a function template
template<typename X>
void doFun(const resolver<X> & )
{
//this function will get executed when C is other than int
//so write your code here, for the general case
}
//this is an overload, not a specialization of the above function template!
void doFun(const resolver<int> & )
{
//this function will get executed when C = int
//so write your code here, for the special case when C = int
}
};
Note an important point : doFun(const resolve<int>& ) is an overloaded function, its not a specialization of the function template. You cannot specialize member function template without specializing the enclosing class template.
Read these articles:
Template Specialization and Overloading
Why Not Specialize Function Templates?
Dispatching on a resolver type as #Nawaz shows is IMHO the best way. Another option would be to move the real implementation of that function outside of the class, inside its own struct, make it static and partially specialize the struct. Inside the class, call that. Of course, if it accesses private parts of myClass, you need to make it friend:
template<class A, class B, class C>
class myClass;
template<class A, class B, class C>
struct myClassFuncs{
typedef myClass<A,B,C> class_type;
static void myFunc(class_type* self){
// generic for everything ...
}
};
template<class A, class B>
struct myClassFuncs<A,B,int>{
typedef myClass<A,B,int> class_type;
static void myFunc(class_type* self){
// specialized on C == int ...
}
};
// and so on ...
template<class A, class B, class C>
class myClass{
typedef myClassFuncs<A,B,C> func_holder;
friend class func_holder;
public:
void myFunc(){
func_holder::myFunc(this);
}
};
Though that leads to a lot of wrappers in the class and the specialized versions...
Another idea, which can be said to be pretty crazy, is to not have functions in the class but functors. Those get specialized and then called. This is more verbose, but allows a better access to which functions you want to specialize. Though, if they want to access private parts, you now need to make all of them friends. :/
template<class A, class B, class C>
class myClass;
template<class A, class B, class C>
class myClass_myFunc{
typedef myClass<A,B,C> class_type;
class_type* const _self;
public:
myClass_myFunc(class_type* self)
: _self(self)
{}
void operator() const{
// generic logic here
}
};
template<class A, class B>
class myClass_myFunc<A,B,int>{
typedef myClass<A,B,int> class_type;
class_type* const _self;
public:
myClass_myFunc(class_type* self)
: _self(self)
{}
void operator() const{
// specialized logic here
}
};
template<class A, class B, class C>
class myClass{
friend class myClass_myFunc<A,B,C>;
public:
myClass()
: myFunc(this)
{}
const myClass_myFunc<A,B,C> myFunc;
};
**I've gotten a few suggestions to make my function pure generic, which would work, but I'd prefer limiting the function to only accept Base and its children.
Having trouble making a function that can accept arguments of a variadic template class base type, while the function will actually be called with classes that derive from Base. I've tried a few things. Here's the general idea. Given:
template<typename... Args> struct Base {
std::tuple<Args...> data;
... //other stuff
};
struct DerivedA : Base<string, int> {
};
struct DerviedB : Base<bool, string, int> {
};
What's the correct way to create a function that does this:
string moosh_together(Base A, Base B) { //I only need access to Base's members
return get<0>(A.data) + get<1>(B.data);
}
main() {
DerivedA aThing;
get<0>(aThing.data) = "foo";
DerivedB bThing;
get<1>(bThing.data) = "bar'd";
cout << moosh_together(aThing, bThing) << endl;
}
Output:
foobar'd
I've tried a few variations of the moosh_together function, none of which work. Leaving it as above generates a compiler error about missing template arguments. I'm unsure how to pass through to the function the template arguments that define DerivedA and DerivedB.
Other things I've tried (shotgun method):
string moosh_together(Base<> A, Base<> B) {}
//err: conversion from 'DerivedA' to non-scalar type 'Base<>' requested
template<Base<typename... Args> T1, Base<typename... Args> T2>
string moosh_together(T1 A, T2 B) {}
//err: expected paramter pack before '...'
template<Base<Args...> T1, Base<Args...> T2>
string moosh_together(T1 A, T2 B) {}
//err: 'Args' was not declared in this scope
Edit:
If you need both parameter packs, you can just put both in the template specification:
template<typename... ArgsA, typename... ArgsB>
string moosh_together(const Base<ArgsA...>& A, const Base<ArgsB...>& B) {
return get<0>(A.data) + get<1>(B.data);
}
This works because the parameter packs are inferred from arguments and not specified in a list. Naturally you can't have a class that depends on multiple parameter packs.
When you write: string moosh_together(Base A, Base B), ask yourself what Base is. Base is a class template, not a class type.
In other words, given:
template <typename T>
struct foo {};
foo<int> and foo<float> are two totally different types, that just so happened to be made from the same class template. They have no common base class, and you cannot refer to them simply as foo anymore than you can refer to both int and float with a single type.
You could factor out the non-dependent parts of Base:
struct Core
{
string name;
};
template <typename... Args>
struct Base : Core
{
// ...
};
And then refer to the Core portions:
// pass by reference, to avoid unnecessary copying
string moosh_together(const Core& a, const Core& b);
Or just make the function totally generic:
template <typename BaseOne, typename BaseTwo>
string moosh_together(const BaseOne& a, const BaseTwo& b);
And say "if you have the necessary members, you can use this function".
Couldn't you create a base class in the inheritance hierarchy for Base and pass that to the moosh_together() function? (Low knowledge of c++ here)
I think the general expansion of this is
string moosh_together(Base<T1...> A1, Base<T2...> A2, ... Base<Tn...> An) {
return get<0>(A1.data) + get<1>(A2) + ... + get<n-1>(An.data);
}
This could be written as follows
template<int I>
string moosh_together() { return ""; }
template<int I, typename ...Base1Ty, typename ... Bases>
string moosh_together(Base<Base1Ty...> const& base1, Bases const&... bases) {
return get<I>(base1.data) + moosh_together<I+1>(bases...);
}
template<typename ... Bases>
string moosh_together(Bases const&... bases) {
return moosh_together<0>(bases...);
}