【问题标题】:C++ "smart" predicate for stl algorithm用于 stl 算法的 C++“智能”谓词
【发布时间】:2009-03-01 17:52:24
【问题描述】:

我需要为 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”命名空间中。

谢谢。

【问题讨论】:

  • 您能否添加一个示例用法来说明行为如何变化?

标签: c++ templates stl sfinae


【解决方案1】:

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

例如

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

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

【讨论】:

  • 是的。我忘了指出这个问题。请稍后阅读编辑部分。
  • 你知道Predicate使用时的具体类型吗?您可以在这些类上模板化 while Finder 结构吗?看来您需要在某个时候指定具体类型。
  • 如果你真的不知道类型,你可以使用类特征之类的东西来弄清楚。它确实需要您为所有涉及的类设置特征,或者至少是常见的基类类型。
  • 你的意思是“bool IsPresent::isPresent(name)”?
  • 我已经用我认为解决问题的方式更新了答案。看看有没有适合你的。没有什么是完美的。最后,您需要以某种方式按类型拆分查找器,这就是您想要产生的管理开销。
【解决方案2】:

您可以查看Veldhuizen's homepageswitch 模板。您可能可以使用它来选择确切的运算符?

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

    • 那些不同的类型没有关系。它们在逻辑和含义上有一些共同点,但它们不在层次结构中。
    【解决方案4】:

    提升不是魔法;使用 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 将删除第二个重载。重载解析将选择第二个,如果它确实编译,因为 ... 是一个更糟糕的匹配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多