Выполнение деталей реализации черт

#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. Я думаю, вы переосмысливаете что-то там, где нет реальной проблемы