Estaba pensando en la especialización de plantillas y me preguntaba si había una forma de usar la especialización parcial para generar dos rutas de código diferentes que se fusionan automáticamente.

En este caso, tengo un motor computacional donde quiero una enumeración para seleccionar diferentes funciones en tiempo de compilación. En este caso, dependiendo de policy o scheme, quiero diferentes funciones en tiempo de compilación.

Estaba pensando que podría evitar la especialización explícita y parcial de todas las variantes. es posible?

He incluido una pequeña muestra de código.

#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;
}
2
Mikhail 24 ago. 2020 a las 08:20

1 respuesta

La mejor respuesta

CRTP y la herencia múltiple pueden ser tus amigos. En el núcleo, puede usar herencia múltiple para traer funcionalidad de ambas clases

template <scheme s, policy p>
struct computational_backend
: scheme_backend<s>
, policy_backend<p>
{ };

Este tipo de cosas funcionará, siempre que las diferentes partes del proceso computacional no necesiten llamarse entre sí. En otras palabras, funciona cuando middle () nunca necesita llamar a left () y viceversa.

Si necesita que se llamen entre sí, el patrón de plantilla curiosamente recursivo (CRTP) es su amigo. Esta es una rareza que básicamente le permite convertir al tipo más derivado de las clases base porque pasa ese tipo más derivado como un parámetro de plantilla. Se parece a esto:

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>
{
    DerivedT& 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>
{ };

Solo dibujé una de las especializaciones, pero entiendes la idea. Resulta que la transmisión estática extraña no solo es legal en C ++, es, de hecho, extraordinariamente rápida. En muchos casos, el compilador puede optimizarlo completamente.

5
Mikhail 24 ago. 2020 a las 23:21