C++中是否有一种方法可以合成函数?

3

一般问题:

如果有两个对象AB,其中分别含有函数 f_A(arg list)f_B(arg list)

创建一个包含由 f_A(...) 和 f_B(...) 组成的函数的对象 C,最好的方法是什么? 例如: f_C() = f_A() + f_B() 或 f_C() = f_A(f_B())

能否重载 "+" 运算符,以便我们可以像这样创建对象 C?

auto object_c = object_a + object_b

这是我的代码示例:

class GaussianKernel : public Kernel {
        public:
            GaussianKernel(double sigma) : m_sigma(sigma), m_scale(1) {}

            double covarianceFunction(
                double   X,
                double   Y
            )
            {
                double result;

                result = m_scale  *  exp(-norm(X - Y) / (m_sigma*m_sigma));

                return result;      
            }


            GaussianKernel operator+(const GaussianKernel& b) {
            /*Here I would like to overload the + operator such that 
            I can create a kernel from two others kernels, 
            I mean with a covariance function compound of the previous ones 
            */
            }
        private:
            double m_sigma;
            double m_scale;
        };

感谢您。

1
一般来说,f_A() + f_B()f_A( f_B() ) 是完全不同的东西,你想要哪一个? - 463035818_is_not_a_number
在这种情况下,您只需要为这些方法的返回类型添加一个 operator+,它们返回什么?在您的示例中,它是 double,这意味着您不必做任何额外的工作。 - 463035818_is_not_a_number
covarianceFunction是从Kernel继承的虚函数吗? - sebrockm
@VTT 我不明白问题在哪里,为什么第三个内核不能是两个其他内核的组合? - Cryckx
@sebrockm 是的,甚至是纯虚拟的。 - Cryckx
显示剩余2条评论
3个回答

3

给定两个方法f_Af_B,您可以使用lambda函数获得f_C,该函数返回其他方法的总和:

auto f_C = [](/*param*/){ return f_A(/*param*/) + f_B(/*param*/); };
auto sum_result = f_C(param);

要获得混合方法,应按照以下步骤进行:
auto f_C = [](/*param*/){ return f_B( f_A(/*param*/)); };
auto compound_result = f_C(param);

PS: 我知道这并不直接适用于你的例子,仍在努力找出你想要做什么。


好的,如果我表达不清楚,我很抱歉。我想要实现核函数(在我的情况下是能够计算两个随机变量之间协方差的对象),并且在数学上,核函数可以按照一些规则(加法、缩放等)与其他核函数组合。因此,我已经创建了一个简单的协方差方法,它返回一个双精度浮点数,并且我想要轻松地创建具有更复杂功能的核函数。 - Cryckx

2
我会从这样的原型解决方案开始:

class FooKernel : public Kernel {
public:
    FooKernel (std::function<double(double, double)> fun) : fun_(fun) {}
    double covarianceFunction(
            double   X,
            double   Y
        ) const {
        return fun_(X, Y);
    }

   template<class T>
   auto operator+(const T &b) const {
       return FooKernel([b, this](double X, double Y){
           return this->covarianceFunction(X, Y) + b.covarianceFunction(X, Y);
       });
   }
private:
    std::function<double(double, double)> fun_;
};    


class GaussianKernel : public Kernel {
    public:
        GaussianKernel(double sigma) : m_sigma(sigma), m_scale(1) {}

        double covarianceFunction(
            double   X,
            double   Y
        ) const 
        {
            double result;
            result = m_scale  *  exp(-norm(X - Y) / (m_sigma*m_sigma));
            return result;      
        }

   template<class T>
   auto operator+(const T &b) const {
       return FooKernel([b, this](double X, double Y){
           return this->covarianceFunction(X, Y) + b.covarianceFunction(X, Y);
       });
   }
    private:
        double m_sigma;
        double m_scale;
};

现在不再使用lambda表达式,而是按您的意愿使用您的函数。

以后我会尝试移除std::function,因为它可能会对性能产生相当大的影响。相反,我会将FooKernel作为类模板存储可调用对象的值。


2
你的方法不可组合,因为你的 operator+ 返回一个 lambda,你不能再次调用 operator+。如果你想要组合三个或更多的内核,该怎么办?auto D = A + B + C; 这样是行不通的。 - 463035818_is_not_a_number
抱歉,也许我错了,它确实可以(B+C返回lambda,并且A + (B+C)应该没问题) - 463035818_is_not_a_number
1
将这个包装到 std::function 中也会有相当严重的性能影响。我绝对不希望这样一个简单的内核计算受到它的影响。 - Max Langhof
1
@MaxLanghof 我在我的代码中也不会这样做,但为了简单起见和减少代码行数,我没有使用模板魔法来实现它。 - bartop

2
我建议创建另一个Kernel的子类:
class CompoundGaussianKernel : public Kernel {
    public:
        CompoundGaussianKernel(GaussianKernel const& kernel1, GaussianKernel const& kernel2) 
            : m_kernel1(kernel1), m_kernel2(kernel2) 
        {}

        double covarianceFunction(double X, double Y)
        {
            return m_kernel1.covarianceFunction(X, Y) + m_kernel2.covarianceFunction(X, Y);
            // or any other composition than "+"
        }

    private:
        GaussianKernel m_kernel1;
        GaussianKernel m_kernel2;
    };

我建议不要在类内定义operator+,而是作为自由函数。

CompoundGaussianKernel operator+(GaussianKernel const& kernel1, GaussianKernel const& kernel2)
{
    return CompoundGaussianKernel(kernel1, kernel2);
}

隐藏继承的covarianceFunction虽然可行,但并不是好的实践。它应该被声明为虚函数。 - Max Langhof
OP没有回答我的问题,即Kernel是否是虚拟的。无论如何,如果是虚拟的,它将隐式地保持虚拟。它将被任何子类覆盖。另一方面,如果它不是虚拟的,它将被子类隐藏,无论子类是否声明为虚拟。 - sebrockm
哦,我的错,你继承的是Kernel而不是GaussianKernel。在这种情况下,它当然和OP的版本一样正确。 - Max Langhof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接