Смешивание шаблонов с std :: enable_if_t и специализацией - PullRequest
4 голосов
/ 18 июня 2020

Edit: выполнено переписывание предоставленного кода на основе отзывов, чтобы помочь проиллюстрировать проблему

Скажем, у меня есть метод template<T> do_foo(T), и я использую его исключительно с использованием std::enable_if для определения возвращаемых значений

Это позволяет мне писать для него подписи, такие как

// templates_top.h

template<typename T>
std::enable_if_t<std::is_fundamental<T>::value, int> do_foo(T bb)
{
    return 0;
}

Я также могу добавить больше определений для do_foo дальше по строке - templates_top.h не требует объявлений fwd для все, что могло go в do_foo - то, что когда-либо вызывает do_foo, просто должно быть.

Теперь предположим, что у меня есть пара классов, для которых реализация do_foo в идеале not быть в шапке

// templates_a.h
#include "./templates_top.h"
#include "./templates_b.h"

class LocalTypeA { 
  public:
    LocalTypeB* internal_;
};

template<typename T>
std::enable_if_t<std::is_same<T, LocalTypeA>::value, int> do_foo(T bb)
{
    // Some detailed business logic 
    // With recursive do_foo for member
    return do_foo<LocalTypeB>(*(bb.internal_));
}

// templates_b.h
#include "templates_top.h"

class LocalTypeB { 
    public: 
        bool the_val = false;
};

template<typename T>
std::enable_if_t<std::is_same<T, LocalTypeB>::value, int> do_foo(T bb)
{
    // Some detailed business logic here 
    // specific to LocalTypeB that ideally isn't in a header
    // Also not that do_foo is called recursively for the std::is_fundamental type
    return do_foo<bool>(bb.the_val);
}

Все это работает нормально, когда все в шапке. Но теперь - мы хотим убрать эту сложную бизнес-логику c из заголовков.

Мы не можем полностью специализировать ее, например, template<> do_foo(LocalTypeB), поскольку не существует общей c реализации template<typename T> do_foo(T). Если мы сделаем этот общий c один - тогда перегрузка std::is_fundamental станет неоднозначной.

Есть ли способ написать этот сценарий, чтобы

  • templates_top.h сохранял его игнорирование LocalTypeA/B и использование std::is_fundamental
  • Разрешение перегрузкам для LocalTypeA/B перемещаться в их собственные соответствующие имп-файлы и при этом разрешать std::enable_if подписанные функции

FWIW - моим целевым стандартом C ++ является C ++ 11, но я предполагаю, что функции из C ++ 14/17 могут сделать это возможным, поэтому они тоже хороши.

Стоит отметить - это довольно большое упрощение реальной проблемы, с объяснением которой у меня возникли некоторые проблемы - я ищу больше способов решить суть конфликта типов в гораздо большем масштабе.

Пример кода здесь - компилировать как g++ main_a.cpp или g++ main_b.cpp

1 Ответ

2 голосов
/ 18 июня 2020

Функции не могут быть частично специализированы.

SFINAE действительно может использоваться для сброса перегрузки.

Кажется, что диспетчеризация тегов здесь более уместна:

template <typename T> struct Tag{};

template <typename T>
auto do_foo(Tag<bool>, T data)
{
    return true;
}

template <typename T>
void do_foo(Tag<void>, T data) = delete;

// ...

template <typename R, typename R>
auto do_foo(T data) -> decltype(do_foo(Tag<R>{}, data))
{
    return do_foo(Tag<R>{}, data);
}
...