¿Cómo puedo expandir la llamada a las clases base de la plantilla variadic?

Tengo un conjunto de políticas no ortogonales, todas implementan un método conocido común, las políticas agregan controles de seguridad. Quiero que los usuarios puedan combinar las políticas para permitir una validación más compleja sin crear políticas para cada caso de combinación a mano. Mi enfoque es crear una nueva clase de política para combinar otras.

El ejemplo simplificado a continuación muestra C como la clase de combinación, aquí el ID del método se combina. El resultado esperado es, cuando se llama a id en C, llamar secuencialmente al ID de cada clase base.

#include  using namespace std; struct A { void id() { cout << "A ";} }; struct B { void id() { cout << "B ";} }; template struct C : public A, public As... { void id() { A::id(); As...::id(); // This line does not work, it is illustrative. } }; int main() { C c; c.id(); //expected: result AB } 

La pregunta es: ¿es posible expandir As … de alguna manera para hacer esto sin usar un enfoque recursivo, simplemente usando el … operador?

    Por supuesto. Necesita un contexto que permita la expansión de paquetes: uno simple es una lista de inicializadores preparados, que también tiene el beneficio de garantizar una evaluación de izquierda a derecha:

     using expander = int[]; (void) expander { 0, ((void) As::id(), 0)... }; 
    • ... expande un patrón a su izquierda; en este caso, el patrón es la expresión ((void) As::id(), 0) .

    • El , en la expresión es el operador de coma, que evalúa el primer operando, descarta el resultado, luego evalúa el segundo operando y devuelve el resultado.

    • El lanzamiento (void) en As::id() existe para protegerse del operator, sobrecargado, y puede omitirse si está seguro de que ninguna de las llamadas As::id() devolverá algo que sobrecargue el operador de coma.
    • 0 en el lado derecho del operador de coma es porque el expander es una matriz de int s, por lo que la expresión completa (que se usa para inicializar un elemento de la matriz) debe evaluarse como int .
    • El primer 0 garantiza que no intentemos crear una matriz de tamaño 0 ilegal cuando As es un paquete vacío.

    Demo


    En C ++ 17 (si tenemos suerte), el cuerpo completo de C::id se puede reemplazar por una expresión de doblez binario : (A::id(), ... , (void) As::id()); Demo