【问题标题】:Template Specialization Function with Eigen arguments具有特征参数的模板专业化函数
【发布时间】:2018-06-06 08:47:21
【问题描述】:

我正在尝试为 Eigen 类型编写一些具有模板专业化的函数,遵循这个问题:Eigen: type deduction in template specialization of base-class

这是我写的:

#include <type_traits>
#include <Eigen/Core>

namespace isEigenPlainObjectBaseDetail {
    template <typename T>
    std::true_type test(const Eigen::PlainObjectBase<T>);
    std::false_type test(...);
}
template <typename T>
struct isEigenPlainObjectBase :
        public decltype(isEigenPlainObjectBaseDetail::test(std::declval<T>())) {};

template <typename T, typename Enable = void>
void foo(T& obj) {
    std::cout << "Generic Called!\n";
}

template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type>
void foo(T& obj) {
    std::cout << "Eigen Specialization Called!";
}

int main() {
    Eigen::MatrixXd m;
    Eigen::VectorXd v;
    int i = 0;
    foo(i);
    foo(m);
    foo(v);
    return 0;
}

但这每次都会调用泛型函数:

Generic Called!
Generic Called!
Generic Called!

我做错了什么?如何为具有特征矩阵和向量的函数编写模板特化?

【问题讨论】:

    标签: c++ c++11 templates eigen sfinae


    【解决方案1】:

    不确定这是唯一的问题(没有安装 Eigen,所以我无法检查;抱歉)但是...

    您可以部分专门化结构和类,而不是函数。

    所以这段代码

    template <typename T, typename Enable = void>
    void foo(T& obj) {
        std::cout << "Generic Called!\n";
    }
    
    template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type>
    void foo(T& obj) {
        std::cout << "Eigen Specialization Called!";
    }
    

    不起作用,我很惊讶编译。

    编辑:正如 Quentin 所指出的(谢谢!)第二个模板参数是非类型的;所以两个foo() 之间没有冲突,但第二个被排除在外,因为非类型模板参数的类型为void。这是不允许的。

    要使用 SFINAE,您应该通过 classstruct;像

    template <typename T, typename = void>
    struct foo
     {
       static void func (T & obj)
        { std::cout << "Generic Called!\n"; }
     };
    
    template <typename T
    struct foo<T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type>
     {
       static void func (T & obj)
        { std::cout << "Eigen Specialization Called!"; } 
     };
    

    并且可以用作

    foo<decltype(i)>::func(i);
    foo<decltype(m)>::func(m);
    foo<decltype(v)>::func(v);
    

    也许你可以定义一个bar() 模板辅助函数

    template <typename T>
    void bar (T & t)
     { foo<T>::func(t); }
    

    只需调用

    bar(i);
    bar(m);
    bar(v);
    

    或者使用标签调度更简单。

    如果你定义了几个foo() 接收不同的附加参数

    template <typename T>
    void foo (T &, std::false_type const &)
     { std::cout << "Generic Called!\n" }
    
    template <typename T>
    void foo (T &, std::true_type const &)
     { std::cout << "Eigen Specialization Called!"; }
    

    您应该能够使用像这样的 bar() 辅助函数选择正确的 foo()

    template <typename T>
    void bar (T & t)
     { foo(t, isEigenPlainObjectBase<T>{}); }
    

    和以前一样,只需调用

    bar(i);
    bar(m);
    bar(v);
    

    [注意:未测试]

    【讨论】:

    • 我认为第二个函数是一个重载,这是有效的,因为第二个模板参数是非类型的。但是,它总是被 SFINAE 淘汰,因为 std::enable_if&lt;&gt;::typevoid,这是不允许的。
    • @Quentin - 我没注意到不是typename = typename std::enable_if,而只是typename std::enable_if;答案修改为明确这一点;谢谢!
    • 好的,很高兴知道! :D 我用 gcc 编译它,没有用 msvc 尝试过。但当然它不起作用!我希望在 Eigen 库中设计的东西允许使用模板区分所有 eigen 普通对象库!
    • @AlessandroMuntoni - 所以,如果我理解正确,你对isEigenPlainObjectBase 有疑问;对不起,但我现在的特征还不够。在最坏的情况下,您可以实现一个类型特征来逐个识别所需的类型。
    • @max66 是的,现在我对我使用的所有特征矩阵类型都有一个专门化,每个专门化都调用一个不同名称的函数。我希望有一个更好、更优雅的函数方式:(
    【解决方案2】:

    如果您只是想为您传入的两种类型专门化一个函数,它可能比您想象的要简单得多。

    当前代码:

    template <typename T, typename Enable = void>
    void foo(T& obj) {
        std::cout << "Generic Called!\n";
    }
    
    template <typename T, typename std::enable_if<isEigenPlainObjectBase<T>::value>::type>
    void foo(T& obj) {
        std::cout << "Eigen Specialization Called!";
    }
    

    新代码:

    template <typename T>
    void foo(T& obj);
    
    template <>
    void foo(Eigen::MatrixXd& obj) {
        std::cout << "Eigen Specialization Called!" << std::endl;
    }
    
    template <>
    void foo(Eigen::VectorXd& obj) {
        std::cout << "Eigen Specialization Called!" << std::endl;
    }
    
    template <typename T>
    void foo(T& obj) {
        std::cout << "Generic Called!" << std::endl;
    }
    

    注意,不是为了测试而编译的。

    另一种方法是为你感兴趣的类型添加一个特征,你会怎么做?

    我推荐的方式如下。 (注意未编译/测试)

    template<typename T>
    struct isEigenTrait;
    
    //specializations for all of the eigen types you are interested in
    template<>
    struct isEigenTrait<Eigen::MatrixXd> {
       typedef std::true_type value;
    };
    
    //...
    
    template<typename T>
    struct isEigenTrait {
       typedef std::false_type value;
    };
    
    template<class T, std::enable_if_t<isEigenTrait<T>::value, true_type> = 0>
    void foo(T& obj) {
        std::cout << "Eigen Specialization Called!" << std::endl;
    }
    
    template<class T, std::enable_if_t<isEigenTrait<T>::value, false_type> = 0>
    void foo(T& obj) {
        std::cout << "Generic Called!" << std::endl;
    }
    

    从 c++ 参考修改的代码应该可以工作。

    为了在一个特征中获得所有特征矩阵,我相信以下应该可行。

    //specializations for all of the eigen types you are interested in
    template<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
    struct isEigenTrait< Eigen::Matrix<Scalar, RowsAtCompileTime, ColsAtCompileTime> > {
       typedef std::true_type value;
    };
    

    您可能需要对要添加 isEigenTrait 的每种模板类型执行相同操作。

    【讨论】:

    • 我已经在使用这个解决方案,但是它导致了大量的代码重复。我使用了很多不同的特征矩阵(行主矩阵或列主矩阵,静态或动态),对于这种情况,我希望通过单一专业化来“捕捉”所有这些矩阵!
    • 如果您真的想这样做,请查看 enable_if en.cppreference.com/w/cpp/types/enable_if 上的文档
    • 见上面修改后的答案。
    • 谢谢!但是,同样以这种方式,我应该为所有模板化的特征矩阵类型编写一个专业化。我试图了解是否有一种方法可以在单个专业化中捕获所有特征矩阵类型(我可能使用 Eigen::PlainObjectBase),但区别于通用模板类型 T...
    • 查看修改后的答案,了解“捕捉”所有具有单一特征的特征矩阵的方法。我不相信在这种情况下使用基类有效。这是因为模板不会像普通的 c++ 代码那样使用对象层次结构衰减,因此这种方法不起作用。如果答案正确,请将此答案标记为正确。
    猜你喜欢
    • 2011-12-07
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 2021-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多