#c #templates #c 17
Вопрос:
Я вообще не вижу, как это можно было бы реализовать.
У меня есть неполный шаблон класса, используемый для «переопределения» некоторых методов.
Это один из рабочих примеров:
template<typename...>
struct make_completion;
struct ServiceTraits
{
using mode_type = ModeType;
using warning_type = Waring;
using error_type = Error;
using iteration_result_type = IterationResu<
using steering_type = Steering;
using sync_tag_t = decltype(asio::use_future);
};
template<typename SignatureT>
struct make_completion<ServiceTraits, SignatureT>
{
template<typename CompletionToken>
auto operator()(ServiceTraits, CompletionToken amp;amp;completionToken)
{
using result_type = asio::async_result<std::decay_t<CompletionToken>, SignatureT>;
using completion_handler = typename result_type::completion_handler_type;
completion_handler handler{ std::forward<CompletionToken>(completionToken) };
result_type result{ handler };
return Completion<result_type, completion_handler>{ std::move(result), std::move(handler) };
}
};
Я хочу собрать traits
класс с помощью наследования, поэтому, например, у меня было бы несколько классов признаков, а затем произвольно объединить их.
Например:
template<typename ResultT, typename HandlerT>
struct completion
{
using result_type = ResultT;
using handler_type = HandlerT;
result_type resu<
handler_type handler;
};
// this would be inherited from
struct asio_traits
{
using sync_tag_t = decltype(asio::use_future);
};
Проблема в том, что я не могу использовать что-то подобное std::enable_if
в специализации.
Это не будет компилироваться:
template<typename DerivedTraits,
typename SignatureT,
typename = std::enable_if_t<std::is_base_of_v<asio_traits, DerivedTraits>>>
struct eos::method::make_completion<DerivedTraits, SignatureT>
{
// impl here...
};
Можно ли как-то использовать условие для специализации шаблона класса?
Комментарии:
1. Обычно вы используете аргумент шаблона фиктивного типа в основном шаблоне,
template<typename Enable = void>
а в ограниченных частичных специализациях вы передадите результатstd::enable_if_t
в качестве аргумента шаблона:struct eos::method::make_completion<std::enable_if_t< ... >> ...
. Однако за пакетом параметров шаблона не может следовать дополнительный параметр шаблона, поэтому это добавляет некоторую сложность. Я бы предложил использовать смешивания для комбинированного признака и предоставить этот параметр шаблона одного типа шаблонуmake_completion
класса.2. @dfrib спасибо вам за ваши предложения. Если вы можете показать пример, я бы счел это законным ответом. На данный момент я просто отделился
asio_traits
какasync_traits
имя зависимого типа. Это достаточно просто
Ответ №1:
Что-то в этом роде, возможно:
template <typename...>
struct make_completion_tag;
template <typename Tag, typename...>
struct make_completion_impl;
template <typename... Ts>
struct make_completion : public make_completion_impl<
typename make_completion_tag<Ts...>::type,
Ts...>
{};
struct asio_traits_based_tag;
template <typename DerivedTraits, typename SignatureT>
struct make_completion_impl<asio_traits_based_tag, DerivedTraits, SignatureT> {
// Your specialization here
};
Теперь осталось только вернуть make_completion_tag::type
правильный тег. Например.
template <typename DerivedTraits, typename SignatureT>
struct make_completion_tag<DerivedTraits, SignatureT> {
using type = std::conditional_t<
std::is_base_of_v<asio_traits, DerivedTraits>,
asio_traits_based_tag, void>;
};
ИЗМЕНИТЬ: немного другой подход, который предоставляет внешнюю точку настройки:
template <typename Tag, typename...>
struct make_completion_impl;
void make_completion_selector(...);
template <typename... Ts>
struct make_completion : public make_completion_impl<
decltype(make_completion_selector(static_cast<Ts*>(nullptr)...)),
Ts...>
{};
Теперь у пользователя есть два варианта. Если типы позволяют, они могут специализироваться make_completion
напрямую:
template <AnotherType>
struct make_completion<SpecificType, AnotherType> {
// Your specialization here
};
В более сложном случае они могут объявить соответствующим образом ограниченную перегрузку make_completion_selector
возврата типа тега и специализироваться make_completion_impl
на этом теге:
struct asio_traits_based_tag;
template <typename DerivedTraits, typename SignatureT>
struct make_completion_impl<asio_traits_based_tag, DerivedTraits, SignatureT> {
// Your specialization here
};
template <typename DerivedTraits, typename SignatureT>
std::enable_if_t<std::is_base_of_v<asio_traits, DerivedTraits>,
asio_traits_based_tag>
make_completion_selector(DerivedTraits*, SignatureT*);
Комментарии:
1. В специализации нет ничего особенного
make_completion_tag<DerivedTraits, SignatureT>
, посколькуDerivedTraits
иSignatureT
они так же универсальны, как и были. Поэтому , хотя вы и сделали так, чтобы это сработалоasio_traits
, вы сделали так, чтобы это не сработало для всех других специализаций. Компилятор не сможет вывести междуasio_traits
иsome_other_traits
«специализациями», если кто-то захочет их добавить. Единственное, что отличаетmake_completion_tag
специализации, — это ряд аргументов шаблона. Что в моем случае всегда равно 2. Тем не менее, спасибо за усилия.2. Если вам нужно больше тегов, вы можете добавить в них больше предложений
std::conditional
. Или у вас может быть куча перегруженных вспомогательных функций, возвращающих тег, выбранный с помощью SFINAE, и сделайте этоusing type=decltype(tag_helper((DerivedTraits*)nullptr, (SignatureT*)nullptr));
3. Зачем мне это нужно? Весь смысл этого подхода состоит в том, чтобы иметь специализации, которые не зависят друг от друга, и базовому шаблону не нужно ничего знать о специализациях. При добавлении тегов
std::conditional
это намерение будет нарушено, так как вам придется объединить все возможные теги вместе, введя зависимости от их заголовков. Цель здесь-удобство для пользователя: нужно было бы написать только одну специализацию, никогда не меняя реализацию.4. Не могли бы вы попросить пользователя объявить перегрузку определенного имени функции, ограниченного SFINAE? Например.
struct my_tag; template <typename D, typename S> std::enable_if_t<some_condition, my_tag> make_completion_selector(D*, S*);
Затем вы могли бы использоватьdecltype(make_completion_selector((Ts*)nullptr ...)
в качестве тега для выбораmake_completion_impl<my_tag, ...>
специализации.5. Суть этого вопроса заключается в том, что я не могу использовать SFINAE при специализации шаблона класса. SFINAE предполагает условие в общем шаблоне. Поэтому, если бы я предположил, что определенный класс признаков должен быть основой параметра шаблона, можно было бы использовать наследование. Это довольно запутанно и, как мне кажется, очень трудно поддерживать. Теперь я использую более простое решение — вложенные типы. Подход с SFINAE потребует определения a
Type
в базовом шаблоне и неявной проверки того, является ли он основой другогоType
или того же по умолчанию… Вы могли бы попробовать это