Можно ли комбинировать частичные специализации шаблонов для создания неявно сгенерированных общих путей кода?

#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. Замечательный ответ! и, похоже, это сработало. Спасибо.