15

Estoy experimentando con las nuevas características de C++ 11. En mi configuración, me encantaría usar constructores heredadores, pero desafortunadamente ningún compilador los implementa aún. Por lo tanto, estoy tratando de simular el mismo comportamiento. Puedo escribir algo como esto:detección de constructores protegidos de la clase base (posiblemente abstracta)

template <class T> 
class Wrapper : public T { 
    public: 
    template <typename... As> 
    Wrapper(As && ... as) : T { std::forward<As>(as)... } { } 
    // ... nice additions to T ... 
}; 

Esto funciona ... la mayor parte del tiempo. A veces, el código que usa la (s) clase (s) Wrapper debe usar SFINAE para detectar cómo se puede construir un Wrapper<T>. Sin embargo, existe la siguiente cuestión: en cuanto a la resolución de sobrecarga, el constructor de Wrapper<T> aceptará cualquier argumento, pero la compilación falla (y esto es no cubierto por SFINAE) si el tipo T no se puede construir utilizando esos.

yo estaba tratando de permitir condicionalmente las diferentes instancias de la plantilla constructor usando enable_if

template <typename... As, typename std::enable_if<std::is_constructible<T, As && ...>::value, int>::type = 0> 
    Wrapper(As && ... as) // ... 

que funciona bien siempre y cuando:

  • el constructor adecuado de T es public
  • T no es abstracto

Mi pregunta es: ¿cómo deshacerse de las dos limitaciones anteriores?

I tratado de superar la primera mediante la comprobación (usando SFINAE y sizeof()) si la expresión new T(std::declval<As &&>()...) está bien formado dentro deWrapper<T>. Pero esto, por supuesto, no funciona, porque la única forma en que una clase derivada puede usar el constructor protegido de su base se encuentra en la lista de inicialización de miembros.

Para la segunda, no tengo ni idea, y es la que necesito más, porque a veces es la Wrapper la que implementa las funciones abstractas de T, lo que la hace un tipo completo.

Quiero una solución que:...

  • es correcto según el estándar
  • obras en cualquiera de gcc-4.6 *, gcc-4.7 * o sonido metálico-3 *

Gracias!

+0

Tengo prisa, pero tal vez http://stackoverflow.com/questions/8984013/can-sfinae-detect-private-access-violations puede ayudar aquí, no contaría con gcc 4.6 hacerlo bien aunque – PlasmaHH

+1

El control de acceso es un poco complicado aquí: si usas 'sizeof()', el compilador verificará toda la expresión, acceso incluido, pero luego se verifica el acceso ** desde el contexto de la expresión **, que falla en el caso de constructores protegidos; todo lo que no sea 'sizeof' funciona solo al nivel de resolución de sobrecarga e inferencia de tipo, por lo que las violaciones de acceso no activarán SFINAE, pero entonces, no veo la manera de hacer algo con un constructor, ya que no se puede pasar como un argumento de plantilla . En cuanto al soporte del compilador, me complacerá si ** cualquier ** de los anteriores acepta el código. –

Respuesta

12

Esto parece funcionar bien en mi GCC local (4.7, cortesía de rubenvb). Sin embargo, GCC on ideone imprime varios errores internos del compilador "implementado".

Tuve que hacer públicos los "detalles de implementación" de la clase Experiment public, porque por alguna razón (que huele como un error), mi versión de GCC se queja de que son privados, aunque solo la clase misma lo use.

#include <utility> 

template<typename T, typename Ignored> 
struct Ignore { typedef T type; }; 

struct EatAll { 
    template<typename ...T> 
    EatAll(T&&...) {} 
}; 

template<typename T> 
struct Experiment : T { 
public: 
    typedef char yes[1]; 
    typedef char no[2]; 

    static void check1(T const&); 
    static void check1(EatAll); 

    // if this SFINAE fails, T accepts it 
    template<typename ...U> 
    static auto check(int, U&&...u) 
    -> typename Ignore<no&, 
     decltype(Experiment::check1({std::forward<U>(u)...}))>::type; 

    template<typename ...U> 
    static yes &check(long, U&&...); 

public: 
    void f() {} 
    template<typename ...U, 
      typename std::enable_if< 
      std::is_same<decltype(Experiment::check(0, std::declval<U>()...)), 
          yes&>::value, int>::type = 0> 
    Experiment(U &&...u):T{ std::forward<U>(u)... } 
    {} 
}; 

// TEST 

struct AbstractBase { 
    protected: 
    AbstractBase(int, float); 
    virtual void f() = 0; 
}; 

struct Annoyer { Annoyer(int); }; 

void x(Experiment<AbstractBase>); 
void x(Annoyer); 

int main() { 
    x({42}); 
    x({42, 43.f}); 
} 

Actualización: El código también funciona en Clang.

+0

Uso muy inteligente de la ambigüedad, debo admitir. Déjame ver si funciona en mi entorno. Por cierto, ¿cómo se trata con los constructores de bases privadas? –

+0

@GrzegorzHerman no se verifican los constructores de bases privadas. Debo admitir :(Entonces pensará que es posible una conversión. –

+0

eso es lo que pensé, pero ese es un problema menor para mí, el más importante es conseguir que funcione para las clases abstractas :) –

Cuestiones relacionadas