3

I have two class templates that must be templates (C++). I just simplified their code to show the essence of the problem. How to pass an object (obj1) from one template (MyClass1) to another object (obj2) from second template (MyClass2)? I tried through the template parameter and the constructor, but I still have compilation errors. How to do it correctly? What is important, I don't know template parameters, therefore solution has be universal, not for specified parameters. Object should be passed by pointer or reference, I don't need its copy.

template<int a, int b>
class MyClass1 {
  public:
    MyClass1() {
        // Do something...
    }

    int foo(int x) {
        return a * x + b;
    }
};
template<double m, double n>
class MyClass2 {
  public:
    MyClass2() {
        // Do something
    }

    double bar(int x) {
        // Do something with x using object of MyClass1 and then with m...
    }

    double zet(int x) {
        // Do something with x using object of MyClass1 and then with n...
    }
};
int main() {
    MyClass1<4, 3> obj1;
    MyClass2<3.14, 2.56> obj2; // <-- How to pass obj1 here???
    // Maybe that way?: MyClass2<3.14, 2.56, obj1> obj2;
    // Or that way?: MyClass2<3.14, 2.56> obj2(obj1);

    obj1.foo(12);
    obj2.bar(1.234);
    obj2.zet(5.678);
}

I'm not sure if this is relevant to this problem, but I'm writing C++ code for AVR in Atmel Studio 7 with standard settings.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
catgiggle
  • 43
  • 5
  • 1
    You can make a templated constructor, inside `MyClass2`, that will accept parameters of type `MyClass1`, and construct the object from it. – Petok Lorand Feb 11 '20 at 08:31
  • 1
    If you want to keep an instance of `MyClass1` as a **member** of `MyClass2` you will likely run into trouble because you make the type dependent on the template arguments of the other type. – Timo Feb 11 '20 at 08:32
  • 1
    Also, floating point template parameters are illegal before C++20. Does this code really compile with your compiler? – Timo Feb 11 '20 at 08:41
  • 1
    Why all these template parameters ? Your compiler will generate a specific class type for each different instantiation of your classes. By taking a look at your code, it seems that what you need is not templates at all but instead field members and pass your parameters in your constructors arguments. – Fareanor Feb 11 '20 at 08:52
  • @Timo, you are right. This simplified code not compile. I just want to show the essence of the problem. In real code, there are no integers, but `uint8_t` port definitions etc. @Fareanor, this is also the reason why this is template. I have one device on some port and pins and other device on other port and pins. And this is fine to have different types for both. Additional advantage is fact that it causes smaller weight of program. – catgiggle Feb 11 '20 at 10:42

2 Answers2

1

Your code does not compile with C++11 because of this:

A non-type template parameter must have a structural type, which is one of the following types (optionally cv-qualified, the qualifiers are ignored):

  • lvalue reference type (to object or to function);
  • an integral type;
  • a pointer type (to object or to function);
  • a pointer to member type (to member object or to member function);
  • an enumeration type;
  • std::nullptr_t; (since C++11)
  • a floating-point type; (since C++20)

Regarding your core problem, you can do the something like this:

template<int m, int n, typename Obj1Type>
class MyClass2 {
    Obj1Type obj1_;

public:
    MyClass2() {
        // Do something
    }

    MyClass2(Obj1Type const& obj1) {
        obj1_ = obj1;
    }

    // ...
};

And then in main:

int main() {
    MyClass1<4, 3> obj1;
    MyClass2<3, 2, MyClass1<4, 3>> obj2(obj1);

    obj1.foo(12);
    obj2.bar(1);
    obj2.zet(5);
}

Check it out live

UPDATE

You could also make use of inheritance and create a simple base class for this purpose:

class BaseMyClass1 {};

template<int a, int b>
class MyClass1 : public BaseMyClass1 {
    // ...
};

template<int m, int n>
class MyClass2 {
    BaseMyClass1 obj1_;

public:
    MyClass2() {
        // Do something
    }

    template <typename Obj1Type>
    MyClass2(Obj1Type const& obj1) {
        obj1_ = obj1;
    }

    // ...
};

And then in main:

int main() {
    MyClass1<4, 3> obj1;
    MyClass2<3, 2> obj2(obj1);

    obj1.foo(12);
    obj2.bar(1);
    obj2.zet(5);
}

This saves you declaring a template in template parameter list. However, this might not be the perfect solution for you because it introduces object slicing.

Check it out live

NutCracker
  • 11,485
  • 4
  • 44
  • 68
  • 1
    Cool! It works. Really thank you. I also tried with interfaces. `MyClass1` implements `MyClass1Interface` and `MyClass2` accepts this interface as parameter of constructor. Same solution as your avoid writing `MyClass1<4, 3>` twice. I know that I can use `typedef`. Unfortunately adding declaration of interface increases program memory usage by 40 bytes which is a lot in comparison to your solution. – catgiggle Feb 11 '20 at 11:01
  • @catgiggle The 2nd example is most definitely not what you want. First of all, it copies the member `obj1_` (which you may or may not want). But more importantly, this introduces object slicing, which you almost never want. – Timo Feb 11 '20 at 11:55
  • @Timo ofc this might not be what OP wants. There is lots of place for improvement. I just mentioned this as one of the possible options – NutCracker Feb 11 '20 at 11:57
  • Yeah! Much better. But this solution have one problem. Compiler complains that he don't know `obj1.foo` inside `obj2.bar`. After declaring virtual foo (`virtual int foo(int x) = 0;`) method inside `BaseMyClass1` he complains that it is pure virtual. Passing by pointer solves this problem, but increases memory usage too much. – catgiggle Feb 11 '20 at 12:23
  • @catgiggle i suppose this is something you haven't shown in your initial question? – NutCracker Feb 11 '20 at 12:28
  • Please see my comment under `MyClass2::bar` in oryginal question. I wrote about using object inside method. Also example code which shows current problem: https://godbolt.org/z/b4LCNq . – catgiggle Feb 11 '20 at 12:36
  • @catgiggle Then you could use `std::shared_ptr` like [here](https://godbolt.org/z/YSUejH) – NutCracker Feb 11 '20 at 13:12
  • @NutCracker, unfortunately I can't: https://www.microchip.com/webdoc/avrlibcreferencemanual/FAQ_1faq_cplusplus.html . It means that I can use pointer or your first solution with template in template. – catgiggle Feb 11 '20 at 13:21
  • @catgiggle i would suggest you to go for a first solution – NutCracker Feb 11 '20 at 13:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207597/discussion-between-catgiggle-and-nutcracker). – catgiggle Feb 11 '20 at 13:59
0

Template class accepts another template class as template parameter. So you can pass typename T for Myclass2. Example program: You can try something like this:

#include <iostream>
using namespace std;

template<typename T1>
class Myclass1
{
    public:
    Myclass1(T1 x):m1_x(x)
    {
        cout << "Myclass1 C'tor" << m1_x << endl;
    }
    T1 m1_x;

    T1 get_x()
    {
        return m1_x;
    }
};

template < typename T1 >
class Myclass2
{
    public:
    Myclass2(T1 x): m_x(x)
    {
        cout << "My class 2 C'tor with value"  << m_x.get_x() << endl;
    }

    public:
    T1 m_x;
};


int main()
{
    Myclass1 <int > obj1(5000);

    Myclass2< Myclass1 <int> > obj2 ( obj1 );
    return 0;
}