перегрузка функции шаблона: enable_if для CRTP, компилятор настаивает на выборе функции generi c - PullRequest
2 голосов
/ 15 апреля 2020

Это модельный случай, когда есть обобщенная c функция func, а затем (гуманно говоря) более специализированная функция func для классов, производных от Base через CRTP, которая включена только для соответствующего типа аргумента через enable_if.

#include<type_traits>
#include<iostream>

// CRTP hierarchy
template<class T> class Base{ };
class Derived: public Base<Derived>{};

// overload 1
template<class T> void func(const T& a){ std::cerr<<"1\n"; }
// overload 2
template<class T, typename std::enable_if<std::is_base_of<Base<T>,T>::value,int>::type* = nullptr>
inline void func(const Base<T>& obj){ std::cerr<<"2\n"; }

int main(void){ func(Derived()); }

Однако компилятор все еще считает, что первая перегрузка лучше подходит. Я понимаю, что enable_if только включает функцию, но не делает ее лучше для разрешения перегрузки.

Я сожалею, что не смог разобраться в Шаблон функции раздел справочника по c ++.

Может кто-нибудь дать совет, как заставить компилятор отдавать предпочтение вторая функция?

Спасибо!

Редактировать: МОТИВАЦИЯ: При реальном использовании эти функции должны обрабатывать различные типы скаляров и массивов (в частности, Eigen, который использует CRTP). Скаляры должны охватывать все типы чисел c, такие как целые числа, числа с плавающей запятой, ... (без их перечисления), а другая перегрузка должна охватывать массивы - опять же, не перечисляя их, но зная, что все они получены из Eigen::DenseBase<Derived>.

Ответы [ 2 ]

3 голосов
/ 15 апреля 2020

Просто используйте constexpr if s, чтобы выбрать правильную функцию из одной функции c.

C ++ 17

namespace
{
    template<typename T>
    inline void base_func( const T& derived );

    template<typename T>
    inline void other_func( const T& otherType );
}

template<typename T>
inline void func( const T& type )
{ 
     if constexpr( std::is_base_of_v<Base<T>, T> )
         base_func( type );
     else
         other_func( type );
}

C ++ 14 Использование черт

namespace
{
    template<typename T>
    inline void base_func( const T& derived );

    template<typename T>
    inline void other_func( const T& otherType );

    template <bool B>
    struct func_select_trait;

    template <>
    struct func_select_trait<true>
    { 
      template <typename T>
      static void call( const T& derived ) { base_func<T>( derived ); }
    };

    template <>
    struct func_select_trait<false>
    {
      template <typename T>
      static void call( const T& otherType ) { other_func<T>( otherType ); }
    }; 

}

template<typename T>
inline void func( const T& type )
{ 
     func_select_trait<std::is_base_of<Base<T>, T>::value>::call<T>( type );
}
3 голосов
/ 15 апреля 2020

2 перегрузки являются жизнеспособными

  • перегрузка 1. имеет точное совпадение
  • перегрузка 2. выведена в базовое преобразование

См .:

https://en.cppreference.com/w/cpp/language/overload_resolution#Ranking_of_implicit_conversion_sequences для получения более подробной информации

Вы можете СФИНАУТЬ первую перегрузку:

// overload 1
template <class T, std::enable_if_t<!std::is_base_of_v<Base<T>, T>, int> = 0>
void func(const T& a){ std::cerr<<"1\n"; }

Демо

...