Pregunta

Necesito diseñar un predicado para algoritmos stl como find_if, count_if.

namespace lib
{
    struct Finder
    {
        Finder( const std::string& name ):
            name_( name )
        {
        }

        template< typename TElement >
        bool operator( const TElement& element )
        {
            return element.isPresent( name_ );
        }

        /* template< typename TElement >
        bool operator( const TElement& element )
        {
            const Data& data = element.getData();
            return data.isPresent( name_ );
        }*/ 
    };
}

Pero necesito tener diferentes operadores () de acuerdo con la presencia de algunos métodos determinados en TElement. Al igual que si tiene " getData " Me gustaría revisar esos datos y si no los hubiera, realizaría otras acciones.

Soy consciente de SFINAE. Pero no tengo impulso :: en el proyecto. Así que, o bien hay una implementación sencilla de la plantilla " has_method " o conoce alguna otra solución de diseño.

No puedo señalar tipos específicos y simplemente sobrecargar porque me gustaría poner este Predicado en el de la biblioteca del proyecto, que no conoce esas clases específicas con " getData " método.

La solución con rasgos de clase es buena en la medida en que no hay espacios de nombres. Buscador de predicados en " lib " espacio de nombres y clase con " getData " está en " programa " espacio de nombres.

Gracias.

¿Fue útil?

Solución

¿Por qué usar plantillas mathods en absoluto? Solo use la clase específica en la que desea basar o una clase base común si hay muchos tipos de clases.

por ejemplo

struct Finder
{
    Finder( const std::string& name ):
        name_( name )
    {
    }

    bool operator( const IsPresentBaseClass& element )
    {
        return element.isPresent( name_ );
    }

    bool operator( const GetDataBaseClass& element )
    {
        const Data& data = element.getData();
        return data.isPresent( name_ );
    } 
 };

Si este patrón ocurre mucho con diferentes tipos de clase y conoce los tipos antes de usar el predicado, puede crear el predicado en sí mismo.

por ejemplo

template<class T1, class T2>
struct Finder
{
    Finder( const std::string& name ):
        name_( name )
    {
    }

    bool operator( const T1& element )
    {
        return element.isPresent( name_ );
    }

    bool operator( const T2& element )
    {
        const Data& data = element.getData();
        return data.isPresent( name_ );
    } 
 };

O otro enfoque que podrías usar es usar algún tipo de rasgos de clase para mantener la información.

por ejemplo

struct UseIsPresent
{
    template<class T>
    static bool CompareElement( const T& element, const std::string& name )
    {
        return element.isPresent( name );
    }
};

struct UseGetData
{
    template<class T>
    static bool CompareElement( const T& element, const std::string& name )
    {
        const Data& data = element.getData();
        return data.isPresent( name );
    } 
};

// default to using the isPresent method
template <class T>
struct FinderTraits
{
    typedef UseIsPresent FinderMethodType;
};

// either list the classes that use GetData method
// or use a common base class type, e.g. UseGetData
template <>
struct FinderTraits<UseGetData>
{
    typedef UseGetData FinderMethodType;
};

struct Finder
{
    Finder( const std::string& name )
    : name_( name )
    {
    }

    template<class T>
    bool operator()( const T& element )
    {
        return FinderTraits<T>::FinderMethodType::CompareElement<T>(element, name_);
    }

    std::string name_;
};

La desventaja de todos estos métodos es que, en algún momento, necesita conocer los tipos para poder dividirlos en qué método usar.

Otros consejos

Puede consultar Página de inicio de Veldhuizen para la plantilla switch . Probablemente puedas usar esto para elegir el operador exacto?

Haga que sus tipos se deriven de " tipos de funcionalidad " (por ejemplo, un tipo " has_function1 ") que funcionará como interfaces Java y tiene una oportunidad, porque SFINAE se puede usar para probar si un tipo se puede convertir en otro.

Si estás interesado, puedo investigarlo y darte una respuesta más detallada.

EDITAR: Sé que dijiste que no tenías bibliotecas de Boost disponibles, pero ¿hay algo que te impida obtener los pocos archivos que se necesitan para obtener un impulso :: is_convertible work? ¡No habría nada en particular para compilar!

Boost no es magia; el uso de SFINAE es bastante sencillo:

    template< typename TElement >
    bool operator( const TElement& element, ... )
    {
        return element.isPresent( name_ );
    }

    template< typename TElement >
    bool operator( const TElement& element, const Data& data = element.getData())
    {
        return data.isPresent( name_ );
    }

SFINAE eliminará la segunda sobrecarga si no se compila. La resolución de sobrecarga elegirá la segunda, si se compila ya que ... es una coincidencia peor.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top