1

So i made a template struct cause i want to be able to decide what type i give to my val. But when creating a function i don't know how to do it. Here's what i'm doing:

In my .hpp

template<typename T>
struct Integer
{
    T val;
    void setUint(const T &input);
};

Now i can set what variable i want in the val and what i want in the function.

But now in my cpp i don't know how to invoke the function.

void Integer<T>::setUint(const T &input)
{
    val = input;
}

Error: identifier "T" is undefined.

Ricardoke
  • 133
  • 1
  • 3
  • 10
  • 2
    Add ```template``` to the beginning of function definition. And move template function definition to .h file – Deumaudit Jul 11 '22 at 11:27
  • 3
    *"now in my cpp"*. See [why-can-templates-only-be-implemented-in-the-header-file](https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). – Jarod42 Jul 11 '22 at 11:28
  • 1
    Read about templates in a [good book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list). (And you're not trying to invoke the function, you're trying to define it.) – molbdnilo Jul 11 '22 at 11:34

3 Answers3

1

A template function is a way to operate with generic types (you may consider the type as an argument). Your template parameter T allows to pass different types to a function when you invoke the function (which means, simply said, you may replace T with some other types int, double, ...)

Please, have a look at the following simple example.

#include <iostream>
#include <typeinfo>

// you may put it all in a hpp and thus include the hpp file in the CPP
template<typename T>
struct Integer
{
    T val;
    void setUint(const T &input){
        val=input;
        std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
    }
};

// or look at Jarod42's implementation details.
/*
template<typename T>
void Integer<T>::setUint(const T &input){
   val=input;
   std::cout <<"the type is " << typeid(T).name() << " value is "<< val << std::endl;
}*/


// and here you have your cpp calling you template function with different types
int main()
{
    Integer<double> value;
    value.setUint(1500000);

    Integer<int> value2;
    value2.setUint(5);
    
    return 0;
}
Pat. ANDRIA
  • 2,330
  • 1
  • 13
  • 27
  • 1
    It is even better to constrain what T may be, class Integer would not make sense for say T == std::string. Note that using typeid(T) as you do here is fine (debug output), but it should not be used to make runtime decissions. – Pepijn Kramer Jul 11 '22 at 12:07
  • With Jarod42's implementation i get an error. `undefined reference to echo_serialization::Integer::setUint(unsigned long const&) ` when trying to define the function out of the header. If i do it the way you did it all goes smooth. Don't understand why its not passing the params. I want to define it out of the header. – Ricardoke Jul 11 '22 at 12:18
  • This is because you are may be trying to put his function implementation in a cpp file and your compiler cannot find it. At first, try to put everything in the same hpp file (as i mentionned in the code) - either use my approach or delete my function implementation and put the one from Jarod outside the struct (but always in the same file) By the way, In your question comment, you have a link explaining why you should put the template implem in the header file – Pat. ANDRIA Jul 11 '22 at 12:59
0

Syntax is

template <typename T>
void Integer<T>::setUint(const T &input)
{
    val = input;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Also for templates, check if T actually matches your expectations. Your template will not make sense for types that are not Integers. Example on how to add these constraints here : https://godbolt.org/z/YsneKe7vj (both C++17 SFINAE, and C++20 concept)

#include <type_traits>
#include <string>

//-----------------------------------------------------------------------------------------------------------
// for C++17 setup a constexpr that will evaluate (at compile time, that's the constexpr bit) 
// if a given type is an integer type
template<typename type_t>
constexpr bool is_integer_type_v = std::is_integral_v<type_t> && !std::is_same_v<type_t,bool>;

// create a template that can optionally be enabled. This is an example of a technique called SFINAE
template<typename type_t, typename enable = void>
struct Integer;

// for C++ define a specialization that will succesfully be enabled of integer types only
template<typename type_t>
struct Integer<type_t,std::enable_if_t<is_integer_type_v<type_t>>>
{
    void Set(const type_t v)
    {
        value = v;
    }

    type_t value;
};

//-----------------------------------------------------------------------------------------------------------
// for C++20 use concepts 

template<typename type_t>
concept is_integer = std::is_integral_v<type_t> && !std::is_same_v<type_t, bool>;

// note is_integer now replaces typename, and it can only accept types
// that satisfy the concept
template<is_integer integer_t>
struct IntegerCpp20
{
    void Set(const integer_t v)
    {
        value = v;
    }

    integer_t value;
};

//-----------------------------------------------------------------------------------------------------------

int main()
{
    //Integer<std::string> value; // will not compile;
    Integer<int> value;
    value.Set(2);

    //IntegerCpp20<std::string> value; // will not compile;
    IntegerCpp20<int> value20;
    value20.Set(20);

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19