Назовите (или обратитесь) специальный параметр шаблона - PullRequest
1 голос
/ 28 апреля 2020

Я решил использовать следующий шаблон для автоматического объединения различных типов объектов и их конфигов:

enum class Type { Car, Person };

template< Type TYPE >
struct Object;

template< Type TYPE >
struct ObjectConfig;

template<>
struct ObjectConfig< Type::Car >
{
};

// Version 1
template<>
struct Object< Type::Car >: public ObjectConfig< Type::Car > // How could I avoid this duplication???
{
};

// Version 2
template<>
struct Object< Type::Car >
{
    static constexpr Type myType{ Type::Car }; // How could I avoid this duplication???
    ObjectConfig< myType > m_params;
};

Он предназначен для защиты от ошибок и автоматизации c, но я не могу избежать написания значение enum дважды (что подвержено ошибкам и не является автоматическим c). Я хотел бы написать что-то подобное (поэтому я бы хотел как-то ссылаться на специализированный параметр):

template< Type TYPE >
struct Object;

template<>
struct Object< Type::Car >: public ObjectConfig< TYPE >
{
};

Есть ли какой-нибудь прием, который я мог бы использовать для достижения чего-то подобного?

Спасибо вам за заранее!

Подробный код вы найдете здесь (код)

Ответы [ 4 ]

1 голос
/ 05 мая 2020

У меня есть решение для обеих версий, хотя оно немного сложнее.

#include <type_traits>

enum class Type { Car, Person, Truck };

template<Type TYPE>
struct ObjectConfig;

template<>
struct ObjectConfig<Type::Car>
{ };

template<>
struct ObjectConfig<Type::Person>
{ };

template<Type T>
struct FakeDependency: public std::false_type
{ };

template<Type TYPE, Type DELEGATED_TYPE = TYPE, typename DUMMY = typename std::enable_if<DELEGATED_TYPE == TYPE>::type >
struct Object;

template<Type TYPE, Type DELEGATED_TYPE, typename DUMMY>
struct Object
{
    static_assert( FakeDependency<DELEGATED_TYPE>::value, "Not supported!" );
};

template<Type DELEGATED_TYPE, typename DUMMY>
struct Object<Type::Car, DELEGATED_TYPE, DUMMY>
{
    ObjectConfig<DELEGATED_TYPE> m_params;
};

template<Type DELEGATED_TYPE, typename DUMMY>
struct Object<Type::Person, DELEGATED_TYPE, DUMMY> : public ObjectConfig<DELEGATED_TYPE>
{ };

int main() {
    Object<Type::Car> c;
    Object<Type::Person> p;
    //Object<Type::Truck> t;  // Static assertion
    //Object<Type::Car, Type::Person> cp; // template 3 (enable_if) invalid
}

Вы можете проверить полный код

Я ввел шаблон, который скрывает первый, имея его в качестве значения по умолчанию. Вы можете использовать его как внутри класса, так и для наследования.

Я добавил проверки безопасности, чтобы предотвратить создание экземпляра вашего объекта с другими параметрами шаблона: Object<Type::Car, Type::Person>. Для этого я добавил третий параметр шаблона, который является шаблоном типа со значением enable_if в качестве значения по умолчанию. Вы получите ошибку во время компиляции, если попытаетесь создать экземпляр объекта с другими параметрами шаблона. Вторая проверка не позволяет вам иметь такие объекты: Object<Type::Car, Object::Car, int>, потому что в этом случае компилятор обратится к основному шаблону, который будет утверждаться.

Без проверки безопасности становится все проще:

enum class Type { Car, Person, Truck };

template<Type TYPE>
struct ObjectConfig;

template<>
struct ObjectConfig<Type::Car>
{ };

template<>
struct ObjectConfig<Type::Person>
{ };

template<Type TYPE, Type DELEGATED_TYPE = TYPE>
struct Object;

template<Type DELEGATED_TYPE>
struct Object<Type::Car, DELEGATED_TYPE>
{
    ObjectConfig<DELEGATED_TYPE> m_params;
};

template<Type DELEGATED_TYPE>
struct Object<Type::Person, DELEGATED_TYPE> : public ObjectConfig<DELEGATED_TYPE>
{ };

int main() {
    Object<Type::Car> c;
    Object<Type::Person> p;
}
0 голосов
/ 08 мая 2020

Я думаю, что нашел универсальное решение.

#include <type_traits>

enum class Type { Car, Person };

template< Type TYPE, typename T = void >
struct Object
{
    static_assert( sizeof( T ) == 0, "Not supported" );
};

template< Type TYPE >
struct ObjectConfig;

template<>
struct ObjectConfig< Type::Car >
{
};


// Version 1 - solved
template< Type TYPE >
struct Object< TYPE, std::enable_if_t< TYPE == Type::Car > >: public ObjectConfig< TYPE > // No emum duplication
{
};

// Version 2 - solved
template< Type TYPE >
struct Object< TYPE, std::enable_if_t< TYPE == Type::Person > >
{
    ObjectConfig< TYPE > m_params; // No emum duplication
};

Вы можете проверить полное решение здесь (код)

0 голосов
/ 30 апреля 2020

Я думаю, что я решил проблему с версией 2:

enum class Type { Car, Person };

template< Type TYPE >
struct Object;

template< Type TYPE >
struct ObjectConfig;

template<>
struct ObjectConfig< Type::Car >
{
};

template< typename T >
struct ObjectTypeResolver
{
};

template< template< Type > class OBJ_T, Type TYPE >
struct ObjectTypeResolver< OBJ_T< TYPE > >
{
    static constexpr auto value{ TYPE };
};

// Version 1 - still unsolved
//template<>
//struct Object< Type::Car >: public ObjectConfig< Type::Car > // How could I avoid this duplication???
//{
//};

// Version 2 - solved
template<>
struct Object< Type::Car >
{
    static constexpr Type myType{ ObjectTypeResolver< Object >::value }; // No emum duplication
    ObjectConfig< myType > m_params;
};

Вы можете проверить полный код здесь (код)

Я использовал следующие: стандарт C ++ 17 17.7.1. Локально объявленные имена

Как и обычные (не шаблонные) классы, шаблоны классов имеют имя-инъектированного класса (раздел 12). Введенное имя класса может использоваться как имя шаблона или имя типа. Когда он используется со списком аргументов шаблона, в качестве аргумента шаблона для параметра шаблона или в качестве окончательного идентификатора в подробном спецификаторе типа объявления шаблона класса друга, он ссылается на сам шаблон класса. , В противном случае это эквивалентно имени шаблона, за которым следуют параметры шаблона шаблона класса, заключенного в <>.

В рамках специализации шаблона класса или частичной специализации, когда имя введенного класса используется как имя типа, оно эквивалентно имени шаблона, за которым следуют аргументы шаблона специализации или частичной специализации шаблона класса, заключенные в <>.

Вот почему это не может быть применимо к версии 1. Вид проблем: наследование списка выходит за рамки шаблона класса.

Эта часть вопроса все еще открыта.

0 голосов
/ 28 апреля 2020

Если возможно, вы можете работать по композиции вместо наследования. Создайте внутренний шаблонный класс конфигурации и добавьте в него члена.

template< Type TYPE>
struct ObjectByComposition
{
    template<typename Type TYPE >
    struct Config{
        /*Whatever*/
    };

    Config<TYPE> m_Config;
};

ObjectByComposition< Type::Car > m_params;

Правка после ответа OP: тот факт, что это внутренний класс, не является обязательным, конечно, важно сделать член m_config с типом T.

template< Type TYPE  >
struct ObjectByComposition
{
    ObjectConfig< TYPE> m_Config;
};

ObjectByComposition< Type::Car > m_params;
...