Search code examples
c++templatesiteratorc++17function-pointers

How to get the type of non-static method without instance?


I am trying to create a template function in C++ like:

template<typename IteratorType>
double evaluate(const IteratorType &rBegin, const IteratorType &rEnd,
    double(*function)( const decltype( *IteratorType ) &rObject )) // error, typename not allowed
    // I'm unsure if I need to put the reference there, not part of the problem.
{
    // ...
} 
// not my actual code, just to show an example.

Though this does not work since I need an instance of a class/struct to call a non-static method, which I can't do since it's in a function. After googling around a bit I found this solution:


double(*function)( const typename std::iterator_traits<IteratorType>::value_type &rObject )

Though as you can see this gets clumsy to use (and is a pain to change), especially when you are trying to create multiple overloads. This also did not seem to work for my iterators, so after a while I realized this:

double(*function)( const typename IteratorType::value_type &rObject)

would be the best solution.

In the end I realized there was no guarantee for "IteratorType" to have value_type defined as the type of the value, and that pointers are a thing.

Is there any way for me to get away with something along the lines of

double(*function)( const decltype(*IteratorType) &robject)

?


Solution

  • First of all you need a minimal example to your problem. Here is what I understood from your comments:

    #include <iostream>
    #include <vector>
    #include <string>
    
    struct MyClass /* final */
    {
        static double member(std::string const& val) /* noexcept */
        {
            std::cout << "double MyClass::member(int val): " << val << "\n";
            return {}; // return appropriate results
        }
    };
    
    template<typename IteratorType>
    void evaluate(IteratorType rBegin, const IteratorType rEnd,
     // WHAT??? other than:
     // double(*function)(const typename std::iterator_traits<IteratorType>::value_type &rObject )
    {
        while (rBegin != rEnd)
        {
            function(*rBegin);
            ++rBegin;
        }
    }
    
    int main()
    {
        std::vector<std::string> vec{ "1", "2", "3"};
        evaluate(vec.cbegin(), vec.cend(), &MyClass::member);
    }
    

    Though as you can see this gets clumsy to use [...]

    In addition to @Const's answer, if your problem is to use std::iterator_traits, you have following two other options.

    • Option - I

      Like @chtz mentioned in the comments, use std::declval to get the underline type of the iterators as follows:

      template<typename IteratorType>
      void evaluate(IteratorType rBegin, const IteratorType rEnd,
       double(*function)(
       std::remove_reference_t<decltype(*std::declval<IteratorType>())> const&))
       //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--> value_type == std::string 
      {
          // ... code 
      }
      
      

      Providing a helper template type alias wouldn't be a bad idea there:

      template<typename IteratorType>
      using ValueType = std::remove_reference_t<decltype(*std::declval<IteratorType>())>;
      
      template<typename IteratorType>
      void evaluate(IteratorType rBegin, const IteratorType rEnd,
         double(*function)(ValueType<IteratorType> const&))
         //                ^^^^^^^^^^^^^^^^^^^^^^^ value_type == std::string
      {
         // ... code
      }
      

      (See live demo)

    • Option - II

      From the rBegin, by converting the T& to T using std::remove_reference_t.

      
      template<typename IteratorType>
      void evaluate(IteratorType rBegin, const IteratorType rEnd,
         double(*function)(std::remove_reference_t<decltype(*rBegin)> const&))
         //                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ---> std::string
      {
         // ... code
      }
      
      

      (See live demo)