2

我需要为 stl 算法设计谓词,例如 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_ );
        }*/ 
    };
}

但是我需要它根据 TElement 中某些特定方法的存在而具有不同的运算符 ()。就像它有“getData”一样,我想检查该数据,如果没有,我会做一些其他的动作。

我知道 SFINAE。但我没有 boost:: 项目。所以要么有一些简单的模板“has_method”实现,要么你知道一些其他的设计解决方案。

我不能指向特定类型并简单地重载,因为我想将此 Predicate 放在项目库之一中,该项目库不知道那些具有“getData”方法的特定类。

只要没有命名空间,具有类特征的解决方案就很好。“lib”命名空间中的谓词查找器和具有“getData”的类位于“program”命名空间中。

谢谢。

4

4 回答 4

3

为什么要使用模板方法?如果有很多类类型,只需使用您想要基于它的特定类或公共基类。

例如

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_ );
    } 
 };

如果这种模式在不同的类类型中经常发生,并且您在使用谓词之前就知道类型,那么您可以模板化谓词本身。

例如

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_ );
    } 
 };

或者您可以使用的另一种方法是使用某种类特征来保存信息。

例如

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_;
};

所有这些方法的缺点是在某些时候您需要知道类型才能将它们拆分为要使用的方法。

于 2009-03-01T18:48:09.117 回答
1

您可以查看Veldhuizen 的模板switch主页。您可能可以使用它来选择确切的运算符?

于 2009-03-01T17:59:50.307 回答
0

让您的类型派生自“功能类型”(例如类型“has_function1”),它将作为 java 接口工作,您就有机会,因为 SFINAE 可用于测试一种类型是否可以转换为另一种类型。

如果您有兴趣,我可以研究一下并给您更详细的答案。

编辑: 我知道你说你没有可用的 Boost 库,但是有什么阻止你获得让 boost::is_convertible 工作所需的几个文件吗?不会有任何特别要编译的东西!

于 2009-03-01T18:03:42.217 回答
0

升压不是魔法;使用 SFINAE 相当简单:

    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 无法编译,它将删除第二个重载。重载解决方案将选择第二个,如果它确实编译,因为 ... 是一个更糟糕的匹配。

于 2009-03-02T09:37:09.180 回答