6

我不愿意说我想不通,但我想不通。我用谷歌搜索并搜索了 Stack Overflow,但结果是空的。

问题的抽象且可能过于模糊的形式是,如何使用特征模式来实例化成员函数? [更新:我在这里使用了错误的术语。它应该是“政策”而不是“特征”。特征描述现有的类。策略规定了合成类。]这个问题是在对我 10 多年前编写的一组多元函数优化器进行现代化改造时提出的。

优化器都通过选择一条远离当前最佳点的直线路径通过参数空间(“更新”),然后在该线上找到更好的点(“线搜索”),然后测试“完成” " 条件,如果没有完成,则迭代。

有不同的方法来进行更新、行搜索,以及可以想象的完成测试和其他事情。连连看。不同的更新公式需要不同的状态变量数据。例如,LMQN 更新需要一个向量,而 BFGS 更新需要一个矩阵。如果评估梯度很便宜,那么线搜索应该这样做。如果没有,它应该只使用函数评估。有些方法比其他方法需要更准确的线搜索。这些只是一些例子。

原始版本通过虚函数实例化了几种组合。通过设置在运行时测试的模式位来选择一些特征。呸。用#define 定义特征并用#ifdef 和宏定义成员函数将是微不足道的。但那是二十年前的事了。让我感到困扰的是,我无法找到一种非常棒的现代方式。

如果只有一个不同的特征,我可以使用奇怪的重复模板模式。但我认为没有办法将其扩展到任意特征组合。

我尝试使用boost::enable_if等。专业的状态信息很容易。我设法完成了这些功能,但只能求助于将this-pointer 作为参数的非友元外部函数。我什至从未想过如何让函数成为朋友,更不用说成员函数了。编译器(VC++ 2008)总是抱怨事情不匹配。我会大喊:“SFINAE,你这个白痴!” 但这个白痴可能是我。

也许标签调度是关键。我还没有深入了解。

当然有可能,对吧?如果是这样,最佳做法是什么?

更新:这是另一个解释它的尝试。我希望用户能够为自定义优化器填写订单(清单),例如从中文菜单中订购 - 一个来自 A 列,一个来自 B 列,等等。服务员,来自 A 列(更新程序) ,我将使用 Cholesky 分解酱更新 BFGS。请从 B 列(线搜索器)开始,我将进行三次插值线搜索,其 eta 为 0.4,rho 为 1e-4。ETC...

更新:好的,好的。这是我做过的游戏。我不情愿地提供它,因为我怀疑这是一种完全错误的方法。它在 vc++ 2008 下运行良好。

#include <boost/utility.hpp>
#include <boost/type_traits/integral_constant.hpp>

namespace dj {

struct CBFGS {
    void bar() {printf("CBFGS::bar %d\n", data);}
    CBFGS(): data(1234){}
    int data;
};

template<class T>
struct is_CBFGS: boost::false_type{};

template<>
struct is_CBFGS<CBFGS>: boost::true_type{};

struct LMQN {LMQN(): data(54.321){}
    void bar() {printf("LMQN::bar %lf\n", data);}
    double data;
};

template<class T>
struct is_LMQN: boost::false_type{};

template<>
struct is_LMQN<LMQN> : boost::true_type{};

// "Order form"
struct default_optimizer_traits {
    typedef CBFGS update_type; // Selection from column A - updaters
};

template<class traits> class Optimizer;

template<class traits>
void foo(typename boost::enable_if<is_LMQN<typename traits::update_type>, 
         Optimizer<traits> >::type& self) 
{
    printf(" LMQN %lf\n", self.data);
}

template<class traits>
void foo(typename boost::enable_if<is_CBFGS<typename traits::update_type>,  
         Optimizer<traits> >::type& self) 
{
    printf("CBFGS %d\n", self.data);
}

template<class traits = default_optimizer_traits>
class Optimizer{
    friend typename traits::update_type;
    //friend void dj::foo<traits>(typename Optimizer<traits> & self); // How?
public:
    //void foo(void); // How???
    void foo() {
        dj::foo<traits>(*this);
    }
    void bar() {
        data.bar();
    }
//protected: // How?
    typedef typename traits::update_type update_type;
    update_type data;
};

} // namespace dj



int main() {
    dj::Optimizer<> opt;
    opt.foo();
    opt.bar();
    std::getchar();
    return 0;
}
4

5 回答 5

2

一个简单的解决方案可能是只使用基于标签的转发,例如

template<class traits>
void foo(Optimizer<traits>& self, const LMQN&) {
    printf(" LMQN %lf\n", self.data.data);
}

template<class traits>
void foo(Optimizer<traits>& self, const CBFGS&) {
    printf("CBFGS %d\n", self.data.data);
}

template<class traits = default_optimizer_traits>
class Optimizer {
    friend class traits::update_type;
    friend void dj::foo<traits>(Optimizer<traits>& self, 
                            const typename traits::update_type&);
public:
    void foo() {
        dj::foo<traits>(*this, typename traits::update_type());
    }
    void bar() {
        data.bar();
    }
protected:
    typedef typename traits::update_type update_type;
    update_type data;
};

或者,如果您想方便地将多个函数组合在一起以获得不同的特征,可能这样的:

template<class traits, class updater=typename traits::update_type> 
struct OptimizerImpl;

template<class traits>
struct OptimizerImpl<traits, LMQN> {
    static void foo(Optimizer<traits>& self) {
        printf(" LMQN %lf\n", self.data.data);
    }
};

template<class traits> 
struct OptimizerImpl<traits, CBFGS> {
    static void foo(Optimizer<traits>& self) {
        printf("CBFGS %d\n", self.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
    friend class traits::update_type;
    friend struct OptimizerImpl<traits>;
public:
    void foo() {
        OptimizerImpl<traits>::foo(*this);
    }
    // ...
};
于 2010-04-15T12:17:54.770 回答
1

我认为模板专业化是朝着正确方向迈出的一步。这不适用于函数,所以我切换到类。我改变了它,所以它修改了数据。我对受保护的成员和结交朋友并不那么感兴趣。没有继承的受保护成员是一种气味。将其公开或提供访问器并将其设为私有。

template <typename>
struct foo;

template <>
struct foo<LMQN>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" LMQN %lf\n", that.data.data);
        that.data.data = 3.14;
    }
};

template <>
struct foo<CBFGS>
{
    template <typename OptimizerType>
    void func(OptimizerType& that)
    {
        printf(" CBFGS %lf\n", that.data.data);
    }
};

template<class traits = default_optimizer_traits>
class Optimizer{
public:
    typedef typename traits::update_type update_type;
    void foo() {
        dj::foo<typename traits::update_type>().func(*this);
    }
    void bar() {
        data.bar();
    }
    update_type data;
};
于 2010-04-15T02:15:01.807 回答
1

用#define 定义特征并用#ifdef 和宏定义成员函数将是微不足道的。但那是二十年前的事了。

尽管可能值得学习新方法,但宏通常是最简单的做事方式,不应仅仅因为它们“旧”而将其作为工具丢弃。如果您查看 boost 中的 MPL 和有关 TMP 的书,您会发现预处理器有很多用途。

于 2010-04-15T16:25:41.703 回答
0

你的使用enable_if有点奇怪。我已经看到它只使用了两种方式:

  • 代替返回类型
  • 作为补充参数(默认)

将其用于实际参数可能会造成严重破坏。

无论如何,绝对可以将它用于成员函数:

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;
public:

  typename boost::enable_if< is_LQMN<update_type> >::type
  foo()
  {
    // printf is unsafe, prefer the C++ way ;)
    std::cout << "LQMN: " << data << std::endl;
  }

  typename boost::enable_if< is_CBFGS<update_type> >::type
  foo()
  {
    std::cout << "CBFGS: " << data << std::endl;
  }


private:
  update_type data;
};

请注意,默认情况下enable_if返回void,这在大多数情况下非常适合作为返回类型。“参数”语法通常是为构造函数保留的,因为那时你没有返回类型可供使用,但通常更喜欢使用返回类型,这样它就不会干扰重载决议。

编辑

如评论中所述,以前的解决方案不起作用。我找不到任何替代使用enable_if,只有“简单”的重载方式:

namespace detail
{
  void foo_impl(const LMQN& data)
  {
    std::cout << "LMQN: " << data.data << std::endl;
  }

  void foo_impl(const CBFGS& data)
  {
    std::cout << "CBFGS: " << data.data << std::endl;
  }
} // namespace detail

template<class traits = default_optimizer_traits>
class Optimizer{
  typedef typename traits::update_type update_type;

public:
  void foo() { detail::foo_impl(data); }

private:
  update_type data;
};

不是enable_if,但它在不Optimizer向所有人暴露内部结构的情况下完成了这项工作。亲吻?

于 2010-04-15T06:25:42.237 回答
0

这是我(OP)想出的。你能让它更酷吗?

主优化器模板类继承策略实现类。它使这些类可以访问它们需要的优化器的受保护成员。另一个优化器模板类将清单拆分为其组成部分并实例化主优化器模板。

#include <iostream>
#include <cstdio>

using std::cout;
using std::endl;

namespace dj {

// An updater.
struct CBFGS {
    CBFGS(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "CBFGS " << protect << endl;
    }   

    // Peek at optimizer's protected data
    int &protect;

};

// Another updater
struct LMQN {
    LMQN(int &protect_)
        : protect(protect_)
    {}

    void update () {
        cout << "LMQN " << protect << endl;
    }

    // Peek at optimizer's protected data
    int &protect;

};

// A line-searcher
struct cubic_line_search {
    cubic_line_search (int &protect2_)
        : protect2(protect2_)
    {}

    void line_search() {
        cout << "cubic_line_search  " << protect2 << endl;
    }   

    // Peek at optimizer's protected data
    int &protect2;

};

struct default_search_policies {
    typedef CBFGS update_type;
    typedef cubic_line_search line_search_type;
};

template<class Update, class LineSearch>
class Opt_base: Update, LineSearch
{
public:
    Opt_base()
        : protect(987654321) 
        , protect2(123456789)
        , Update(protect)
        , LineSearch(protect2)
    {}
    void minimize() {
        update();
        line_search();
    }

protected:
    int protect;
    int protect2;
};

template<class Search_Policies=default_search_policies>
class Optimizer: 
    public Opt_base<typename Search_Policies::update_type
                  , typename Search_Policies::line_search_type
                    >
{};

} // namespace dj



int main() {
    dj::Optimizer<> opt; // Use default search policies
    opt.minimize();

    struct my_search_policies {
        typedef dj::LMQN update_type;
        typedef dj::cubic_line_search line_search_type;
    };

    dj::Optimizer<my_search_policies> opt2;
    opt2.minimize();

    std::getchar();
    return 0;
}
于 2010-04-16T18:11:32.793 回答