Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. Template class as parameter question

Template class as parameter question

Scheduled Pinned Locked Moved Solved C++ Gurus
6 Posts 3 Posters 519 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • J Offline
    J Offline
    JonB
    wrote on 6 Nov 2024, 10:01 last edited by JonB 11 Jun 2024, 10:12
    #1

    I believe I understand template classes. I believe I know that when you write QList<int> foo you really get a "new class created at compile time" for this (or something like that, I don't think it matters here).

    I have a question about (trying) to use a template class "as is", without caring about what its T type is. Maybe as QList itself does. But I want to use it for a parameter type to an (unrelated) function, I am not trying to derive from it or have it as its own class.

    Let's say I want to write a "helper" function somewhere which takes a QList parameter of any type and (for example) just wants to return the index for a random element. So I want to write something like:

    int randomIndex(QList list)
    {
        return randomNumberUpTo(list.count());
    }
    

    Note that this function makes no reference to the T type of a template/generic QList<T>, and as far as I can see should not care about it (as per QList itself). How can I write this (both in the .h and the .cpp if they differ)?

    Once I have the answer to that. Let's say I change my mind and want it to return a random element (or reference to such). Now I want something like:

    T randomElement(QList<T> list)
    {
        int num = randomNumberUpTo(list.count());
        return list.at(num);
    }
    

    How do you write this one? I realise (I believe) this one will need to know what T to generate code, so it may be rather different from the first case. In fact I am hoping the first one can be done with knowing what T is where this one presumably cannot do so.

    Please remember these are only "helper" functions somewhere. They are to work on any QList parameter.. We are not talking about deriving a class from QList and writing code in that subclass.

    1 Reply Last reply
    0
    • J Offline
      J Offline
      J.Hilk
      Moderators
      wrote on 6 Nov 2024, 12:35 last edited by
      #2

      I'm sorry I don't get what exactly your problem now is ?

      your:

      T randomElement(QList<T> list)
      {
          int num = randomNumberUpTo(list.count());
          return list.at(num);
      }
      

      is pretty spot on: only the template type missing:

      template <typename T>
      int randomIndex(const QList<T>& list) {
          return randomNumberUpTo(list.count());
      }
      

      and

      template <typename T>
      const T & randomIndex(const QList<T>& list) {
          T num =  randomNumberUpTo(list.count());
          return list.at(num);
      }
      

      There is no cpp file case, templates are headers only :D

      You don't need to know what type T is, you can query it to make sure you're actually dealing with an expected type for example:

      #include <type_traits> // für std::is_arithmetic
      
      template <typename T>
      int randomIndex(const QList<T>& list) {
          static_assert(std::is_arithmetic<T>::value, "randomIndex requires a numeric type in QList");
      
          return randomNumberUpTo(list.count());
      }
      

      But you don't have to. You should, but we should do many things and don't :P


      Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


      Q: What's that?
      A: It's blue light.
      Q: What does it do?
      A: It turns blue.

      J 1 Reply Last reply 6 Nov 2024, 12:50
      3
      • J J.Hilk
        6 Nov 2024, 12:35

        I'm sorry I don't get what exactly your problem now is ?

        your:

        T randomElement(QList<T> list)
        {
            int num = randomNumberUpTo(list.count());
            return list.at(num);
        }
        

        is pretty spot on: only the template type missing:

        template <typename T>
        int randomIndex(const QList<T>& list) {
            return randomNumberUpTo(list.count());
        }
        

        and

        template <typename T>
        const T & randomIndex(const QList<T>& list) {
            T num =  randomNumberUpTo(list.count());
            return list.at(num);
        }
        

        There is no cpp file case, templates are headers only :D

        You don't need to know what type T is, you can query it to make sure you're actually dealing with an expected type for example:

        #include <type_traits> // für std::is_arithmetic
        
        template <typename T>
        int randomIndex(const QList<T>& list) {
            static_assert(std::is_arithmetic<T>::value, "randomIndex requires a numeric type in QList");
        
            return randomNumberUpTo(list.count());
        }
        

        But you don't have to. You should, but we should do many things and don't :P

        J Offline
        J Offline
        JonB
        wrote on 6 Nov 2024, 12:50 last edited by
        #3

        @J-Hilk
        Hi, thanks. Quite agree your examples work, and are just what was wanted.

        I am learning (a bit more!) about templates. I cannot recall exactly what I actually had when I typed in my sample code here. It might (well) be that at that time I was trying to put the body/definition of the template functions/methods inside the .cpp. I prefer all my code blocks in .cpps instead of .hs. I have now read how we cannot do that for templates (unless a complex hack of some intermediate extra .cpp file, which we won't go into).

        So it was straightforward at least in .h file, and I mark your answer as solution, thanks.

        S 1 Reply Last reply 7 Nov 2024, 09:33
        1
        • J JonB has marked this topic as solved on 6 Nov 2024, 12:50
        • J JonB
          6 Nov 2024, 12:50

          @J-Hilk
          Hi, thanks. Quite agree your examples work, and are just what was wanted.

          I am learning (a bit more!) about templates. I cannot recall exactly what I actually had when I typed in my sample code here. It might (well) be that at that time I was trying to put the body/definition of the template functions/methods inside the .cpp. I prefer all my code blocks in .cpps instead of .hs. I have now read how we cannot do that for templates (unless a complex hack of some intermediate extra .cpp file, which we won't go into).

          So it was straightforward at least in .h file, and I mark your answer as solution, thanks.

          S Offline
          S Offline
          SimonSchroeder
          wrote on 7 Nov 2024, 09:33 last edited by
          #4

          @JonB said in Template class as parameter question:

          I prefer all my code blocks in .cpps instead of .hs.

          I have seen people use .tpps for template implementations. Those could be included inside the correspoding .hs. If you know all the possible types for a template at compile time, you can mark them as extern in the header file and use explicit instantiation in the .cpp file.

          Again (as I answered in a different post), modules come to the rescue. I haven't played with modules yet, but I believe you can split templates inside modules into separate files.

          Back to your original problem, modern C++ has a lot of features I haven't used. In your concrete case "concepts" come to mind. You could have a QListConcept and then write int randomIndex(auto QListConcept &list);. The STL has some concepts for their different kind of containers, but I am not aware of concepts specifically for Qt. You can also not write a concept for QList, but instead use a requires clause for your function. For randomIndex() the only requirement is to be able to call a member method count() on the object passed into the function. (I am not entirely sure about the syntax, but it is something along the lines of template<class T> requires(T t) { t.count(); } int randomIndex(const T &list);.) Written this way, you could pass in any other container that has this method. Even better if you are using size() instead of count() (both are available in Qt) because then you could even use this function with STL containers besides just Qt containers. That being said: I am against writing general purpose code from the get go. Even though it is nice if you could use it for many different use cases, it makes it a little more complicated to read (and write bugfree and debug). It is thus unnecessary if you are using this function only with QList for the next decade. If the case arises that you want to reuse this function, only then you should think about refactoring it to use more complex concepts.

          J 1 Reply Last reply 7 Nov 2024, 10:14
          0
          • S SimonSchroeder
            7 Nov 2024, 09:33

            @JonB said in Template class as parameter question:

            I prefer all my code blocks in .cpps instead of .hs.

            I have seen people use .tpps for template implementations. Those could be included inside the correspoding .hs. If you know all the possible types for a template at compile time, you can mark them as extern in the header file and use explicit instantiation in the .cpp file.

            Again (as I answered in a different post), modules come to the rescue. I haven't played with modules yet, but I believe you can split templates inside modules into separate files.

            Back to your original problem, modern C++ has a lot of features I haven't used. In your concrete case "concepts" come to mind. You could have a QListConcept and then write int randomIndex(auto QListConcept &list);. The STL has some concepts for their different kind of containers, but I am not aware of concepts specifically for Qt. You can also not write a concept for QList, but instead use a requires clause for your function. For randomIndex() the only requirement is to be able to call a member method count() on the object passed into the function. (I am not entirely sure about the syntax, but it is something along the lines of template<class T> requires(T t) { t.count(); } int randomIndex(const T &list);.) Written this way, you could pass in any other container that has this method. Even better if you are using size() instead of count() (both are available in Qt) because then you could even use this function with STL containers besides just Qt containers. That being said: I am against writing general purpose code from the get go. Even though it is nice if you could use it for many different use cases, it makes it a little more complicated to read (and write bugfree and debug). It is thus unnecessary if you are using this function only with QList for the next decade. If the case arises that you want to reuse this function, only then you should think about refactoring it to use more complex concepts.

            J Offline
            J Offline
            JonB
            wrote on 7 Nov 2024, 10:14 last edited by JonB 11 Jul 2024, 10:17
            #5

            @SimonSchroeder said in Template class as parameter question:

            If you know all the possible types for a template at compile time, you can mark them as extern in the header file and use explicit instantiation in the .cpp file.

            Yeah, I came across that one and that's why I wrote earlier " (unless a complex hack of some intermediate extra .cpp file, which we won't go into)."!

            "concepts" come to mind.

            And wtf are these? (Yes I could Google!)

            template<class T> requires(T t) { t.count(); }

            requires(T t) OMG! I am only just starting on template without extra stuff!

            Written this way, you could pass in any other container that has this method.

            What happens when another container has a different definition of count() which does not return an int (or number)? Maybe your syntax is not quite correct and it would have to be requires(T t) { int t.count(); }?

            I am against writing general purpose code from the get go

            Hmph! But that's is just what e.g. QList is, and loads of other containers. Maybe you will argue different semantics, but template is a huge part of modern C++ in practice and I would call templates an example of "general purpose code".

            My personal overall observation/usage. I loved the simplicity/clarity of C. I totally accept the need to use C++ now and (despite what you may think!) feel I am comfortable with it. However through choice I prefer to use the more "straightforward" stuff in it. I am especially bugged by the ever growing complexity of the syntax as more and more new "features" are added with each release. It just gets so complicated to read, never mind write. So I don't go using new/additional stuff till I find a need to.

            S 1 Reply Last reply 8 Nov 2024, 07:41
            0
            • J JonB
              7 Nov 2024, 10:14

              @SimonSchroeder said in Template class as parameter question:

              If you know all the possible types for a template at compile time, you can mark them as extern in the header file and use explicit instantiation in the .cpp file.

              Yeah, I came across that one and that's why I wrote earlier " (unless a complex hack of some intermediate extra .cpp file, which we won't go into)."!

              "concepts" come to mind.

              And wtf are these? (Yes I could Google!)

              template<class T> requires(T t) { t.count(); }

              requires(T t) OMG! I am only just starting on template without extra stuff!

              Written this way, you could pass in any other container that has this method.

              What happens when another container has a different definition of count() which does not return an int (or number)? Maybe your syntax is not quite correct and it would have to be requires(T t) { int t.count(); }?

              I am against writing general purpose code from the get go

              Hmph! But that's is just what e.g. QList is, and loads of other containers. Maybe you will argue different semantics, but template is a huge part of modern C++ in practice and I would call templates an example of "general purpose code".

              My personal overall observation/usage. I loved the simplicity/clarity of C. I totally accept the need to use C++ now and (despite what you may think!) feel I am comfortable with it. However through choice I prefer to use the more "straightforward" stuff in it. I am especially bugged by the ever growing complexity of the syntax as more and more new "features" are added with each release. It just gets so complicated to read, never mind write. So I don't go using new/additional stuff till I find a need to.

              S Offline
              S Offline
              SimonSchroeder
              wrote on 8 Nov 2024, 07:41 last edited by
              #6

              @JonB said in Template class as parameter question:

              What happens when another container has a different definition of count() which does not return an int (or number)? Maybe your syntax is not quite correct and it would have to be requires(T t) { int t.count(); }?

              I guess this would be some additional requirement using std::is_same_v and decltype(t.count()). I'm still on C++17, so I haven't used concepts myself.

              @JonB said in Template class as parameter question:

              Hmph! But that's is just what e.g. QList is, and loads of other containers.

              A container class is an obvious example where a general purpose implementation makes sense. But, you usually don't implement your own containers. Furthermore, most of the time I would have at least int and double in mind for containers. So, making them general purpose from the get go is a given. But, I don't have to make all my algorithms general purpose such that they might be reusable decades down the line if ever. If there is a current need for general purpose code, make it general purpose. Otherwise it just make maintenance more complicated while being unnecessary. I guess it might also introduce additional bugs because you try to be extra clever.

              1 Reply Last reply
              0

              6/6

              8 Nov 2024, 07:41

              • Login

              • Login or register to search.
              6 out of 6
              • First post
                6/6
                Last post
              0
              • Categories
              • Recent
              • Tags
              • Popular
              • Users
              • Groups
              • Search
              • Get Qt Extensions
              • Unsolved