Priority when choosing overloaded template functions in C++

后端 未结 6 427
不思量自难忘°
不思量自难忘° 2020-12-09 03:35

I have the following problem:

class Base
{
};

class Derived : public Base
{
};

class Different
{
};

class X
{
public:
  template 
  stat         


        
相关标签:
6条回答
  • 2020-12-09 04:17

    Just typecast derived to base

    X::func((Base*)&derived)

    it works....

    0 讨论(0)
  • 2020-12-09 04:18

    I was looking to setup priorities on overlapping enable_if's, specifically for falling back on calling STL container methods, where my traits were things such as is_assignable is_insterable etc.. with which there is overlap on a number of containers.

    I wanted to prioritise assign, if it existed, else use an insert iterator. This is a generic example of what I came up with (modified with infinite levels of priority by some handy folk in the #boost irc channel). It works as the implicit conversion of the priority level ranks the overload below another that is otherwise an equally valid option - removing the ambiguity.

    #include <iostream>
    #include <string>
    
    template <std::size_t N>
    struct priority : priority<N - 1> {};
    
    template <>
    struct priority<0> {};
    
    using priority_tag = priority<2>;
    
    template <typename T> 
    void somefunc(T x, priority<0>)
    {
        std::cout << "Any" << std::endl;
    }
    
    template <typename T> 
    std::enable_if_t<std::is_pod<T>::value >
    somefunc(T x, priority<2>)
    {
        std::cout << "is_pod" << std::endl;
    }
    
    template <typename T>
    std::enable_if_t<std::is_floating_point<T>::value >
    somefunc(T x, priority<1>)
    {
        std::cout << "is_float" << std::endl;
    }
    
    int main()
    {
        float x = 1;
        somefunc(x, priority_tag{});
        int y = 1;
        somefunc(y, priority_tag{}); 
        std::string z;
        somefunc(z, priority_tag{});
        return 0;
    }
    

    It was also suggested that in C++ 14 I could just use constexpr if statements to achieve the same thing, which was far cleaner if Visual Studio 2015 supported them. Hopefully this will help someone else.

    #include <iostream>
    #include <string>
    
    template <typename T>
    void somefunc(T x)
    {
        if constexpr(std::is_floating_point<T>::value) {
          static_assert(std::is_floating_point<T>::value);
          std::cout << "is_float" << std::endl;
        } else if constexpr(std::is_pod<T>::value) {
          static_assert(std::is_pod<T>::value);
          std::cout << "is_pod" << std::endl;
        } else {
          static_assert(!std::is_floating_point<T>::value);
          static_assert(!std::is_pod<T>::value);
          std::cout << "Any" << std::endl;
        }
    }
    
    int main()
    {
        float x = 1;
        somefunc(x);
        int y = 1;
        somefunc(y); 
        std::string z;
        somefunc(z);
        return 0;
    }
    

    // thanks to k-ballo @ #boost!

    0 讨论(0)
  • 2020-12-09 04:28

    I found a VERY easy solution!

    class Base
    {
    };
    
    class Derived : public Base
    {
    };
    
    class Different
    {
    };
    
    class X
    {
    private:
      template <typename T>
      static const char *intFunc(const void *, T *data)
      {
        // Do something generic...
        return "Generic";
      }
    
      template <typename T>
      static const char *intFunc(const Base *, T *data)
      {
        // Do something specific...
        return "Specific";
      }
    
    public:
      template <typename T>
      static const char *func(T *data)
      {
        return intFunc(data, data);
      }
    };
    

    This works great and is very slim! The trick is to let the compiler select the correct method by the (otherwise useless) first parameter.

    0 讨论(0)
  • 2020-12-09 04:39

    You must use SFINAE for this. In the following code, the first function can be instantiated if and only if you pass something that can't be (implicitly) converted to Base *. The second function has this reversed.

    You might want to read up on enable_if.

    #include <iostream>
    #include <boost/utility/enable_if.hpp>
    #include <boost/type_traits.hpp>
    
    class Base {};
    class Derived : public Base {};
    class Different {};
    
    struct X
    {
        template <typename T>
        static typename boost::disable_if<boost::is_convertible<T *, Base *>,
            const char *>::type func(T *data)
        {
            return "Generic";
        }
    
        template <typename T>
        static typename boost::enable_if<boost::is_convertible<T *, Base *>,
            const char *>::type func(T *data)
        {
            return "Specific";
        }
    };
    
    int main()
    {
        Derived derived;
        Different different;
        std::cout << "Derived: " << X::func(&derived) << std::endl;
        std::cout << "Different: " << X::func(&different) << std::endl;
    }
    
    0 讨论(0)
  • 2020-12-09 04:42

    If you are using boost, you can do it with some template metaprogramming:

    #include <boost/type_traits/is_base_of.hpp>
    
    class X
    {
    private:
        template <typename T>
        static const char *generic_func(T *data)
        {
            // Do something generic...
            return "Generic";
        }
    
        template <typename T>
        static const char *base_func(T *data)
        {
            // Do something specific...
            return "Specific";
        }
    
    public:
        template <typename T>
        static const char* func(T* data)
        {
            if (boost::is_base_of<Base, T>::value)
                return base_func(data);
    
            return generic_func(data);
        }
    };
    

    The is_base_of metafunction is evaluated at compile time and the optimizer will most probably remove the dead branch of the if in the func function. This approach allows you to have more than one specific case.

    0 讨论(0)
  • 2020-12-09 04:42

    The expression:

    X::func(derived)
    

    Means that the compiler will generate a declaration and code that effectively has this signature:

    static const char *func(Derived *data);
    

    which turns out to be a better match than your:

    static const char *func(Base *data);
    

    The template function will be used for anything that is legal for typename, e.g. any class you use as T and it will effectively exclude the Base version of your function from being used, due to compile time policy.

    My suggestion is to use specialization in X for your specific types, i.e.:

    template <typename T>
      static const char *func(T *data)
      {
        // Do something generic...
        return "Generic";
      }
    
    template <>
      static const char *func(Derived *data) // 'Derived' is the specific type
      {
        // Do something specific...
        return "Specific";
      }
    

    Hope that works!

    0 讨论(0)
提交回复
热议问题