#c #c 11 #templates #traits
#c #c 11 #шаблоны #Трейты
Вопрос:
Изначально у меня есть набор некоторых функций StandardInfo и вспомогательный шаблон-оболочка StandardTraits, полный статических методов, делающих что-то со StandardInfo:
#include <iostream>
#include <type_traits>
#include <cstdint>
template <std::uint32_t Id>
struct StandardInfo
{ };
template <>
struct StandardInfo<1>
{
using Field1 = std::integral_constant<std::uint32_t, 42>;
};
template <>
struct StandardInfo<2>
{
using Field1 = std::integral_constant<std::uint32_t, 19>;
};
// and more StandardInfo specifications...
template <std::uint32_t Id>
struct StandardTraits
{
using Info = StandardInfo<Id>;
static void DoFoo() { std::cout << Info::Field1::value; }
};
int main()
{
StandardTraits<1>::DoFoo();
return 0;
}
Предположим, мне нужно ввести другую черту нестандартного типа. Все специализации StandardInfo генерируются автоматически, и я не должен изменять этот порядок вещей. Итак, я должен ввести нестандартную черту. Поэтому мне нужна оболочка NonStandardTraits, содержащая те же методы, что и в StandardTraits. Оказывается, что стандартные черты и нестандартные черты отличаются только псевдонимом типа Info. Копирование-вставка реализации кажется сомнительной, поэтому, похоже, мне нужно выполнить реализацию оболочки в виде TraitsImpl и не изменять существующий код, который уже использует StandardTraits.
#include <iostream>
#include <type_traits>
#include <cstdint>
template <std::uint32_t Id>
struct StandardInfo
{ };
template <>
struct StandardInfo<1>
{
using Field1 = std::integral_constant<std::uint32_t, 42>;
};
template <>
struct StandardInfo<2>
{
using Field1 = std::integral_constant<std::uint32_t, 19>;
};
// and more StandardInfo specifications...
template <std::uint32_t Id>
struct NonStandardInfo
{ };
template <>
struct NonStandardInfo<1>
{
using Field1 = std::integral_constant<int, -1>;
};
template <>
struct NonStandardInfo<2>
{
using Field1 = std::integral_constant<int, -2>;
};
template <typename Info>
struct TraitsImpl
{
static void DoFoo() { std::cout << Info::Field1::value; }
};
template <std::uint32_t Id>
struct StandardTraits: TraitsImpl<StandardInfo<Id> >
{
};
template <std::uint32_t Id>
struct NonStandardTraits: TraitsImpl<NonStandardInfo<Id> >
{
};
int main()
{
StandardTraits<1>::DoFoo();
NonStandardTraits<1>::DoFoo();
return 0;
}
Но разве это не раскрывает детали реализации (через общедоступное наследование)? Наследование частных вызовов и прокси-серверов тоже выглядит глупо. Есть ли какой-либо более элегантный подход?
Комментарии:
1. либо я что-то совершенно неправильно понимаю, либо ваша черта — это в основном только «детали реализации», так что плохого в их раскрытии? Скрывать нечего, или я что-то упускаю?
2. @user463035818, это то, что определенно беспокоит меня с точки зрения «хорошего кода» — предоставление стандартных и нестандартных признаков в качестве прямых общедоступных наследников TraitsImpl. Разве я не должен скрывать этот факт?
3. кого волнует, наследуется ли одно от другого? Просто в качестве примера, я бы, возможно, даже написал
NonStandardInfo<2>
какtemplate <> struct StandardInfo<2> : std::integral_constant<std::uint32_t, 19> {};
вместо использованияusing
4. Я думаю, вы переосмысливаете что-то там, где нет реальной проблемы