#c #templates #template-meta-programming
#c #шаблоны #шаблон-метапрограммирование
Вопрос:
Я думал о специализации шаблонов и задавался вопросом, есть ли способ использовать частичную специализацию для генерации двух разных путей кода, которые автоматически объединяются.
В этом случае у меня есть вычислительный движок, где я хочу, чтобы перечисление выбирало различные функции во время компиляции. В этом случае, в зависимости от policy
или scheme
, мне нужны разные функции во время компиляции.
Я думал, что мог бы избежать явной частичной специализации всех вариантов. Возможно ли это?
Я включил небольшой пример кода.
#include <iostream>
enum class scheme { linear, polynomial };
enum class policy { no_checking, raise_exception };
struct computational_base
{
void left();
void middle();
void do_stuff()
{
left();
middle();
}
};
template <scheme scheme, policy left>
struct computational_backend : public computational_base
{
};
template <policy left>
struct computational_backend<scheme::linear, left> : public computational_base
{
void middle()
{
std::cout << "scheme::linear" << std::endl;
}
};
template <scheme scheme>
struct computational_backend<scheme, policy::no_checking> : public computational_base
{
void left()
{
std::cout << "policy::no_checking" << std::endl;
}
};
int main()
{
//Ideally would select middle() from first template, and left() from second template
// more than one partial specialization matches the template argument list
computational_backend<scheme::linear, policy::no_checking> what;
what.do_stuff();
return 0;
}
Ответ №1:
CRTP и множественное наследование могут быть вашими друзьями. По сути, вы можете использовать множественное наследование для обеспечения функциональности из обоих классов
template <scheme s, policy p>
struct computational_backend
: scheme_backend<s>
, policy_backend<p>
{ };
Такого рода вещи будут работать, пока различным частям вычислительного процесса не нужно вызывать друг друга. Другими словами, это работает, когда middle () никогда не нужно вызывать left(), и наоборот.
Если вам нужно, чтобы они вызывали друг друга, любопытно рекурсивный шаблон шаблона (CRTP) — ваш друг. Это странность, которая в основном позволяет вам приводить к наиболее производному типу из базовых классов, потому что вы передаете этот наиболее производный тип в качестве параметра шаблона. Это выглядит следующим образом:
template <typename DerivedT, scheme s>
struct scheme_backend;
template <typename DerivedT, policy p>
struct policy_backend;
template <typename DerivedT>
struct scheme_backend<DerivedT, scheme::linear>
{
DerivedTamp; derived()
{
return *static_cast<DerivedT*>(this);
}
void left()
{
...
derived().middle();
...
}
};
template <scheme s, policy p>
struct computational_backend
: scheme_backend<computational_backend<s, p>, s>
, policy_backend<computational_backend<s, p>, p>
{ };
Я нарисовал только одну из специализаций, но вы уловили идею. Оказывается, что странное статическое приведение не только законно в C , но и, по сути, необычайно быстро. Во многих случаях компилятор может полностью оптимизировать его.
Комментарии:
1. Замечательный ответ! и, похоже, это сработало. Спасибо.