-1

I have structure template with two objects and a template class. I want to be able to have this struct as one of the types that the class object can be and I want the class's functions to be able to manipulate them. At the same types, I want this class to manipulate standard CPP types like int and float.

template<typename T>
struct user_type
{
    T obj1;
    T obj2;
};

template<class T>
class array
{
    int length;
    T* ar_ptr;
    
    public:

    void setArray();
};

template<class T> void array<T>::setArray()
{
    for(int i = 0; i < length; ++i)
    {
        (*(ar_ptr+ i)).obj1 = 0;
        (*(ar_ptr+ i)).obj2 = 0;
    }
}

template <typename T>
void array<T>::setArray()
{
    for(int i = 0; i < length; ++i)
    {
        *(ar_ptr+ i) = 0;
    }
}

I tried to give it two different declarations to the set function but I am getting the following error:

error: redefinition of 'setArray'

error: member reference base type 'float' is not a structure or union

What is the right way to declare this function and instantiate it? I want the same class to manipulate a struct and a start CPP type.

Mr. Noob
  • 47
  • 5
  • How many *definitions* (implementations) of the `setArray` functions do you have? What's the difference between the two you have? Why do you have two? – Some programmer dude Apr 30 '23 at 06:58
  • I need atleast two. One to set the values of the struct and the other to set the values of standard CPP types. I have mentioned both above – Mr. Noob Apr 30 '23 at 07:00
  • You can't have a single function do different things. For that you need two different functions. Like *specialization* showed in [the answer below](https://stackoverflow.com/a/76139955/440558). – Some programmer dude Apr 30 '23 at 07:10
  • @Someprogrammerdude error: use of class template 'user_type' requires template arguments. Getting this error. Since the struct is also a template when I tried your suggestion – Mr. Noob Apr 30 '23 at 07:20
  • Side note, consider changing : `T* arr_ptr` to `std::unique_ptr arr_ptr;` which is the recommended way for current C++ (avoid naked new/delete). You can initialize any type T to its default value using {} or T{}. So you could do `*(ar_ptr) = T{};` – Pepijn Kramer Apr 30 '23 at 07:29
  • @PepijnKramer Since this is an array, `std::unique_ptr` should be used, not `std::unique_ptr`. However the description of the question doesn't state whether `array` is supposed to be a class owning the array or simply a view of an array. In the latter case the use of a raw pointer is perfectly acceptible. – fabian Apr 30 '23 at 07:51
  • @fabian You are right ;) – Pepijn Kramer Apr 30 '23 at 07:56
  • ot: https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three – 463035818_is_not_an_ai Apr 30 '23 at 08:24

2 Answers2

2

You seem to have a conflict in your design, or you misunderstand your assignment/exercise or design.... The type array<T> can't hold user_type<T> objects. Perhaps you're misunderstand the template type T which can be different for both (and every other) templates?

I guess what you really need and want is something like this:

template<typename T>
struct user_type
{
    T obj1;
    T obj2;
};

template<typename T>
class array
{
public:
    // Other public members...

    void setArray()
    {
        for (size_t i = 0; i < length; ++i)
        {
            ar_ptr[i] = {};
        }
    }

private:
    T* ar_ptr;
    size_t length;
};

Then for "normal" types you create you array instance like "normal":

array<int> int_array;

For your user_type template you use an instantiated user_class template as the type (this example uses user_type<int> to make both members of the structure int variables):

array<user_type<int>> user_type_int_array;

In both cases you can call setArray and the "right" thing should happen:

int_array.setArray();  // Default initialize (to zero) all elements of the array

user_type_int_array.setArray();  // Default initialize all user_type<int> elements
                                 // the obj1 and obj2 should be default initialized
                                 // which for user_type<int> means the will become zero
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thank you for your answer. This will work for default value initialization. Can you also point to wear I need to modify if I want to initialize it to random values? Something like array[i] = 10 for some standard type and array[i] = {10, 20} for the user_types – Mr. Noob Apr 30 '23 at 07:29
  • 1
    @Mr.Noob Usually you'd create an overload or a new member function and use copy assignment. You could even implement both in one go using a default parameter: `void setArray(T const& value = T{}) { std::fill(ar_ptr, ar_ptr + length, value); }` – fabian Apr 30 '23 at 07:35
  • 2
    @Mr.Noob You might want to take a look like how [`std::vector`](https://en.cppreference.com/w/cpp/container/vector) works. Or at least use its interface as a reference. – Some programmer dude Apr 30 '23 at 07:54
0

How do I write a template function that handles a struct as well as CPP type like float?

Method 1: C++17

One way is to use constexpr if and std::is_arithmetic_v as shown below:

template<class T> void array<T>::setArray()
{
    if constexpr(std::is_arithmetic_v<T>)
    {
           //code for int, char, double etc
    }
    else
    {
        //code for other types 
        for(int i = 0; i < length; ++i)
        {
            (*(ar_ptr+ i)).obj1 = 0;
            (*(ar_ptr+ i)).obj2 = 0;
        }
    }
    
}

Demo


Method 2: C++20

This uses requires clause.


template<class T>
class array
{
    int length;
    T* ar_ptr;
    
    public:

    void setArray() requires(std::is_arithmetic_v<T>);
    void setArray() requires(!std::is_arithmetic_v<T>);
};
//code for arithmetic types
template<class T> void array<T>::setArray() requires(std::is_arithmetic_v<T>)
{
    
    
}


template<class T> void array<T>::setArray() requires(!std::is_arithmetic_v<T>)
{
    
    
}

working demo

Jason
  • 36,170
  • 5
  • 26
  • 60
  • This won't help my case. I need the second function to work for double, int, char as well. I don't want to have 3 more different specializations for it. – Mr. Noob Apr 30 '23 at 07:08
  • error: use of class template 'user_type' requires template arguments. Getting this error. Since the struct is also a template – Mr. Noob Apr 30 '23 at 07:19
  • @Mr.Noob See my modified program/answer that use `is_arithmetic` to check for `int`, `char`, `double` etc and `if constexpr` as you want. See [working demo](https://godbolt.org/z/TqMav97d8) – Jason Apr 30 '23 at 07:26
  • @Mr.Noob There are 2 ways to do this. Both are shown in my updated answer. Check out the updated answer. – Jason Apr 30 '23 at 07:34
  • This works, but is hard to extend. -1. Sometimes going with the good ol' stuff is a better alternative than using the new stuff. E.g. in this case a traits class or e.g. a function `setToDefault` taking a reference to the element that could be overloaded would make it much easier to extend this.... – fabian Apr 30 '23 at 07:45
  • If an answer shows an approach that I'd consider bad practice, I'd consider it "Not useful". Without mentioning the drawbacks that's not the only problem: An inexperienced programmer could use this kind of code without a second thought decreasing code quality without even being aware of it... – fabian Apr 30 '23 at 08:28