У меня довольно интересная проблема: у меня есть два класса шаблонов. Один может принимать любой параметр шаблона, другой — более специализированный (для этой игрушечной задачи мы скажем, что он должен принимать числа с плавающей запятой).
template< class T >
class CanBeAnything{ /*...*/ };
template< class T >
class MustBeFloat{ static_assert(is_floating_point<T>::value, ""); /*...*/ };
Теперь у меня есть еще один класс шаблонов Foo. Foo не имеет ограничений на свой параметр шаблона и функцию foo, которая принимает CanBeAnything
или MustBeFloat
того же типа. Я надеюсь использовать здесь явное создание экземпляра шаблона, поэтому я хочу, чтобы перегрузка MustBeFloat
существовала только тогда, когда мой параметр шаблона имеет значение с плавающей запятой.
Простейшее решение, по-видимому, состоит в том, чтобы специализировать Foo, но мне не нравится идея дублирования интерфейса между двумя классами. Я придумал почти работающее решение CRTP с одной проблемой, о которой я упомяну через минуту.
/* Traits object to get the value_type out of foo */
template<class FooType>
class FooTraits{};
/* Helper parent class with floating-point only methods */
template<class Derived, bool isFloatingPoint>
class FooSpecialization {}
template<class Derived>
class FooSpecialization<Derived, true>
{
typedef typename FooTraits<Derived>::value_type value_type;
public:
void foo( MustBeFloat<value_type> & x );
};
/* Front-end interface */
template<class T>
class Foo : public FooSpecialization< Foo<T>, is_floating_point<T>::value >
{
typedef FooSpecialization< Foo<T>, is_floating_point<T>::value > Parent;
typedef typename FooTraits< Foo<T> >::value_type value_type;
public:
void foo( CanBeAnything<value_type> & x );
private:
friend class Parent;
};
template<class T>
class FooTraits< Foo<T> >
{ public: typedef T value_type; };
Итак, вот проблема: как есть, вызовы foo( MustBeFloat<value_type> & )
скрыты в дочернем классе путем сокрытия имени, и компилятор выдает мне ошибку «Нет соответствующего вызова метода foo». Если я добавлю строку using Parent::foo;
, чтобы уменьшить ее, я получу ошибку «foo не существует в родительском классе» при создании экземпляра Foo
без плавающей запятой, поскольку метод не существует до сих пор.
Любые идеи? Я согласен со всем этим решением, если доступно более элегантное/рабочее решение.
РЕДАКТИРОВАТЬ: Просто чтобы уточнить: здесь я делаю явное создание экземпляра, поэтому мне нужно, чтобы метод существовал только в том случае, если у меня есть параметр шаблона с плавающей запятой.
template class Foo<int>;
template class Foo<float>;
Это создает экземпляр КАЖДОГО члена класса, поэтому методы, которые полагаются на то, что не создаются экземпляры определенных методов, не подходят.
РЕДАКТИРОВАТЬ2: Хорошо, так что я подумал об этом. Вот решение, с которым я собираюсь:
template<class T>
class Foo
{
public:
template<class T2>
void foo( MustBeFloat<T2> & x ){ static_assert( std::is_same<T,T2>::value, ""); /* ... */}
void foo( CanBeAnything<T> & x ){ /* ... */ }
};
template class Foo<int>;
template class Foo<float>;
template void Foo::foo<float>(MustBeFloat<float> &);
И все это работает. Ура! Спасибо людям, которые помогли мне найти это решение и придумали другие, более изобретательные.
foo(CanBeAnything<value_type>&)
вFooSpecialization
. - person Oktalist   schedule 01.05.2014foo(CanBeAnything<value_type>&)
вFooSpecializationBase
. Шесть одних, полдюжины других. - person Oktalist   schedule 01.05.2014