user7431424
user7431424

Reputation:

Using template methods inside template classes

Why is it expecting Struct1 and how do I format it so I can modify the properties in any struct?

I have 1 template class I'm using to modify data in 3 structs containing the same property names, they just happen to use different data types.

I'm trying to use a template method inside my template class, everything seems to be working as I expected it to with the template class upon the various instantiations.

GenericClass<Struct1> inventory[1000];
GenericClass<Struct2> machines[1000];
GenericClass<Struct3> physicalStructures[1000];

(yes I'm aware vectors exist, play along, I am including as little code as possible) That said, when I try to change one of the properties of the structs, I get an error stating that it is expecting the struct, rather than taking any datatype I hand it.

Main.cpp:39:28: error: no viable conversion from 'int' to 'Struct1'
inventory[0].setIdentifier(1);

I've checked these 3 references (and a few more), but they don't appear to do what I am in any of them. How to define template function within template class in *.inl file

Template class with template function

https://isocpp.org/wiki/faq/templates

Here is a sample of my code.

template <class Type>
class GenericClass
{ 
private:
  Type Identifier;
public:
  void setIdentifier(Type Param);
  Type getIdentifier();
};


/* Struct prototypes
************************/
struct Struct1
{
private:
  int Identifier;
  string Description;
  float Value;
}

struct Struct2
{
private:
  long int Identifier;
  string Description;
  float Value;
};

struct Struct3
{
private:
 string Identifier;
 string Description;
 double Value;
};


int main()
{
  GenericRecord<Struct1> inventory[1000];
  GenericRecord<Struct2> machines[1000];
  GenericRecord<Struct3> physicalStructures[1000];
  inventory[0].setIdentifier(1);
}


template <class Type>
void GenericRecord<Type>::setIdentifier(Type Param)
{
  Identifier = Param;
}
template <class Type>
Type GenericRecord<Type>::getIdentifier()
{
  return Identifier;
}

The return method doesn't work either, I'm expecting that they are both failing for similar reasons.

Again, why is it expecting Struct1 and how do I format it so I can modify the properties in any struct?

Upvotes: 3

Views: 1166

Answers (3)

James Adkison
James Adkison

Reputation: 9602

Why is it expecting Struct1

Because that is the type it was told to use.

Given the template:

template<typename Type>
class GenericClass
{
private:
    Type Identifier;

public:
    void setIdentifier(Type Param);
    Type getIdentifier();
};

The instantiation with type Struct1 (GenericClass<Struct1>) generates the following implementation:

// Just for illustration
class GenericClassStruct1
{
private:
    Struct1 Identifier;

public:
    void setIdentifier(Struct1 Param);
    Struct1 getIdentifier();
};

The above class has replaced all occurrences of Type with Struct1. Now, it should be easy to see why calling setIdentifier expects a Struct1 and not an int.

how do I format it so I can modify the properties in any struct

There is more than one way to do this. What is the "right" way will depend on the constraints of your problem but following example shows one way.

Example

Working Example

#include <iostream>

// Declared but not defined, specializations will provide definitions
template<typename T>
struct GenericTypeTraits;

template<typename T>
class Generic
{
public:
    using IdType = typename GenericTypeTraits<T>::IdType;

    void setIdentifier(IdType id)
    {
        mType.mIdentifier = id;
    }

    IdType getIdentifier() const
    {
        return mType.mIdentifier;
    }

private:
    T mType;
};

class Type1
{
public:
    int mIdentifier;
};

template<>
struct GenericTypeTraits<Type1>
{
    using IdType = int;
};

class Type2
{
public:
    std::string mIdentifier;
};

template<>
struct GenericTypeTraits<Type2>
{
    using IdType = std::string;
};

int main()
{
    Generic<Type1> t1[5];
    t1[0].setIdentifier(3);
    std::cout << t1[0].getIdentifier() << "\n";

    Generic<Type2> t2[5];
    t2[0].setIdentifier("3");
    std::cout << t2[0].getIdentifier() << "\n";

    return 0;
}

Output

3
3

Upvotes: 2

Peter
Peter

Reputation: 36597

The templated GenericClass defines a family of classes. Each specialisation of GenericClass has its own single specialisation of setIdentifier(). So GenericClass<Struct1> has a single setIdentifer() that accepts a Struct1 but does not have a setIdentifier() that accepts an int (or any other type). In the usage inventory[0].setIdentifier(1), inventory[0] is of type GenericClass<Struct1>, and its setIdentifier() member function can accept a Struct1 but not an int. Hence the error message.

If I understand what you're trying to do (I'm not sure - only guessing based on your non-working code) you need to do something like;

template<class T> class Struct
{
   public:
      void SetIdentifier(const T &id) {Identifier = id;};
      T getIdentifier() const {return Identifier;};
    private:
       T Identifier;
       std::string Description;
       float Value;
};

template <class Type>
class GenericClass
{ 
   private:
       Struct<Type> data;
   public:
      void setIdentifier(Type Param) {data.setIdentifier(Param);};
      Type getIdentifier() const {return data.getIdentifier();};
};

int main()
{
      GenericClass<int> inventory[1000];
      inventory[0].setIdentifier(1);
}

Upvotes: 1

Davis Herring
Davis Herring

Reputation: 39768

Identifier in the problematic methods refers to GenericRecord::Identifier, which is one of your structs, not its member named Identifier. Similarly, Type is that struct type, not the type of its Identifier member. So your methods never use the “properties” you’re trying to wrap.

The simple solution involves another template parameter for the property’s type:

template<class T,class P>
class GenericRecord {
  T wrapped;      // avoid confusing reuse of "Identifier"
public:
  void setIdentifier(P p) {wrapped.Identifier=p;}
  P getIdentifier() const {return wrapped.Identifier;}
};

Upvotes: -1

Related Questions