C++ CRTP接口:实现中的不同返回类型

C++ CRTP接口:实现中的不同返回类型,c++,templates,eigen,return-type,crtp,C++,Templates,Eigen,Return Type,Crtp,注意: 在解释和示例中,我使用的是eigen库。然而,我的问题可能会被不熟悉该库的人概括和理解,例如,用std::string\u视图替换ConstColXpr,用std::string替换向量 问题: 我想使用CRTP创建一个接口,其中两个类继承自CRTP,在调用某些成员函数时有以下不同: 第一个类返回数据成员的视图(一个特征::矩阵::ConstColXpr) 第二个类没有此数据成员。相反,当调用函数并返回时(作为Eigen::Vector)会计算适当的值 两种返回类型具有相同的维度(例

注意: 在解释和示例中,我使用的是
eigen
库。然而,我的问题可能会被不熟悉该库的人概括和理解,例如,用
std::string\u视图
替换
ConstColXpr
,用
std::string
替换
向量

问题: 我想使用CRTP创建一个接口,其中两个类继承自CRTP,在调用某些成员函数时有以下不同:

  • 第一个类返回数据成员的视图(一个
    特征::矩阵::ConstColXpr
  • 第二个类没有此数据成员。相反,当调用函数并返回时(作为
    Eigen::Vector
    )会计算适当的值
两种返回类型具有相同的维度(例如2x1列向量)和相同的接口,即可以以完全相同的方式进行交互。这就是为什么我认为将函数定义为接口的一部分是合理的。 但是,我不知道如何在基类/接口中正确定义/限制返回类型<代码>自动编译和执行很好,但不会告诉用户预期的结果

可以更清晰地定义接口吗?我尝试使用
std::invoke\u result
实现函数,但是我必须在接口之前包含继承类型,这是非常落后的。它并不比
auto
好多少,因为实际的类型仍然需要在实现中查找

一个很好的答案应该是一个通用的
特征类型,其中维度是清晰的。但是,我不希望对接口函数的调用需要模板参数(我必须使用
Eigen::MatrixBase
),因为已经有依赖于接口的代码了。 另一个很好的答案是允许两种不同返回类型的构造,但不必知道完整的派生类型。但欢迎所有答案和其他反馈

下面是说明问题的代码:

#include <Eigen/Dense>
#include <type_traits>
#include <utility>
#include <iostream>

template<typename T>
class Base
{
public:
    auto myFunc(int) const;

protected:
    Base();
};

template<typename T>
Base<T>::Base() {
    /* make sure the function is actually implemented, otherwise generate a
     * useful error message */
    static_assert( std::is_member_function_pointer_v<decltype(&T::myFuncImp)> );
}

template<typename T>
auto Base<T>::myFunc(int i) const {
    return static_cast<const T&>(*this).myFuncImp(i);
}


using Matrix2Xd = Eigen::Matrix<double,2,Eigen::Dynamic>;

class Derived1 : public Base<Derived1>
{
private:
    Matrix2Xd m_data;

public:
    Derived1( Matrix2Xd&& );

private:    
    auto myFuncImp(int) const -> Matrix2Xd::ConstColXpr;
    friend Base;
};

Derived1::Derived1( Matrix2Xd&& data ) :
    m_data {data}
{}

auto Derived1::myFuncImp(int i) const -> Matrix2Xd::ConstColXpr {
    return m_data.col(i);
}


class Derived2 : public Base<Derived2>
{
private:
    auto myFuncImp(int) const -> Eigen::Vector2d;
    friend Base;
};

auto Derived2::myFuncImp(int i) const -> Eigen::Vector2d {
    return Eigen::Vector2d { 2*i, 3*i };
}

int main(){
    Matrix2Xd m (2, 3);
    m <<
        0, 2, 4,
        1, 3, 5;

    Derived1 d1 { std::move(m) };

    std::cout << "d1: " << d1.myFunc(2).transpose() << "\n";

    Derived2 d2;

    std::cout << "d2: " << d2.myFunc(2).transpose() << "\n";

    return 0;
}

好的,我想我找到了一个合理可读的解决方案。我们仍然欢迎反馈。 我刚刚定义了另一个模板参数
bool
,它告诉派生类是否保存数据,并使用
std::conditional
bool
定义了返回类型:

#include <Eigen/Dense>
#include <type_traits>
#include <utility>
#include <iostream>


using Matrix2Xd = Eigen::Matrix<double,2,Eigen::Dynamic>;
using Eigen::Vector2d;


template<typename T, bool hasData>
class Base
{
public:
    auto myFunc(int) const ->
        std::conditional_t<hasData, Matrix2Xd::ConstColXpr, Vector2d>;

protected:
    Base();
};

template<typename T, bool hasData>
Base<T, hasData>::Base() {
    static_assert( std::is_member_function_pointer_v<decltype(&T::myFuncImp)> );
}

template<typename T, bool hasData>
auto Base<T, hasData>::myFunc(int i) const ->
std::conditional_t<hasData, Matrix2Xd::ConstColXpr, Vector2d> {
    return static_cast<const T&>(*this).myFuncImp(i);
}



class Derived1 : public Base<Derived1, true>
{
private:
    Matrix2Xd m_data;

public:
    Derived1( Matrix2Xd&& );

private:    
    auto myFuncImp(int) const -> Matrix2Xd::ConstColXpr;
    friend Base;
};

Derived1::Derived1( Matrix2Xd&& data ) :
    m_data {data}
{}

auto Derived1::myFuncImp(int i) const -> Matrix2Xd::ConstColXpr {
    return m_data.col(i);
}


class Derived2 : public Base<Derived2, false>
{
private:
    auto myFuncImp(int) const -> Eigen::Vector2d;
    friend Base;
};

auto Derived2::myFuncImp(int i) const -> Eigen::Vector2d {
    return Eigen::Vector2d { 2*i, 3*i };
}

int main(){
    Matrix2Xd m (2, 3);
    m <<
        0, 2, 4,
        1, 3, 5;

    Derived1 d1 { std::move(m) };

    std::cout << "d1: " << d1.myFunc(2).transpose() << "\n";

    Derived2 d2;

    std::cout << "d2: " << d2.myFunc(2).transpose() << "\n";

    return 0;
}
#包括
#包括
#包括
#包括
使用Matrix2Xd=特征::矩阵;
使用本征::矢量2D;
模板
阶级基础
{
公众:
自动myFunc(int)常量->
std::有条件的;
受保护的:
Base();
};
模板
Base::Base(){
静态断言(std::is_member_function_pointer_v);
}
模板
自动基::myFunc(int i)const->
std::有条件的{
返回静态_cast(*this).myFuncImp(i);
}
类Derived1:公共基
{
私人:
Matrix2Xd m_数据;
公众:
Derived1(Matrix2Xd&&);
私人:
自动myFuncImp(int)const->Matrix2Xd::ConstColXpr;
朋友群;
};
Derived1::Derived1(Matrix2Xd&数据):
m_data{data}
{}
自动派生1::myFuncImp(int i)const->Matrix2Xd::ConstColXpr{
返回m_data.col(i);
}
派生类2:公共基
{
私人:
自动myFuncImp(int)const->Eigen::Vector2d;
朋友群;
};
自动派生2::myFuncImp(int i)const->Eigen::Vector2d{
返回本征::向量2d{2*i,3*i};
}
int main(){
Matrix2Xd-m(2,3);

好的,我想我找到了一个合理可读的解决方案。欢迎反馈。 我刚刚定义了另一个模板参数
bool
,它告诉派生类是否保存数据,并使用
std::conditional
bool
定义了返回类型:

#include <Eigen/Dense>
#include <type_traits>
#include <utility>
#include <iostream>


using Matrix2Xd = Eigen::Matrix<double,2,Eigen::Dynamic>;
using Eigen::Vector2d;


template<typename T, bool hasData>
class Base
{
public:
    auto myFunc(int) const ->
        std::conditional_t<hasData, Matrix2Xd::ConstColXpr, Vector2d>;

protected:
    Base();
};

template<typename T, bool hasData>
Base<T, hasData>::Base() {
    static_assert( std::is_member_function_pointer_v<decltype(&T::myFuncImp)> );
}

template<typename T, bool hasData>
auto Base<T, hasData>::myFunc(int i) const ->
std::conditional_t<hasData, Matrix2Xd::ConstColXpr, Vector2d> {
    return static_cast<const T&>(*this).myFuncImp(i);
}



class Derived1 : public Base<Derived1, true>
{
private:
    Matrix2Xd m_data;

public:
    Derived1( Matrix2Xd&& );

private:    
    auto myFuncImp(int) const -> Matrix2Xd::ConstColXpr;
    friend Base;
};

Derived1::Derived1( Matrix2Xd&& data ) :
    m_data {data}
{}

auto Derived1::myFuncImp(int i) const -> Matrix2Xd::ConstColXpr {
    return m_data.col(i);
}


class Derived2 : public Base<Derived2, false>
{
private:
    auto myFuncImp(int) const -> Eigen::Vector2d;
    friend Base;
};

auto Derived2::myFuncImp(int i) const -> Eigen::Vector2d {
    return Eigen::Vector2d { 2*i, 3*i };
}

int main(){
    Matrix2Xd m (2, 3);
    m <<
        0, 2, 4,
        1, 3, 5;

    Derived1 d1 { std::move(m) };

    std::cout << "d1: " << d1.myFunc(2).transpose() << "\n";

    Derived2 d2;

    std::cout << "d2: " << d2.myFunc(2).transpose() << "\n";

    return 0;
}
#包括
#包括
#包括
#包括
使用Matrix2Xd=特征::矩阵;
使用本征::矢量2D;
模板
阶级基础
{
公众:
自动myFunc(int)常量->
std::有条件的;
受保护的:
Base();
};
模板
Base::Base(){
静态断言(std::is_member_function_pointer_v);
}
模板
自动基::myFunc(int i)const->
std::有条件的{
返回静态_cast(*this).myFuncImp(i);
}
类Derived1:公共基
{
私人:
Matrix2Xd m_数据;
公众:
Derived1(Matrix2Xd&&);
私人:
自动myFuncImp(int)const->Matrix2Xd::ConstColXpr;
朋友群;
};
Derived1::Derived1(Matrix2Xd&数据):
m_data{data}
{}
自动派生1::myFuncImp(int i)const->Matrix2Xd::ConstColXpr{
返回m_data.col(i);
}
派生类2:公共基
{
私人:
自动myFuncImp(int)const->Eigen::Vector2d;
朋友群;
};
自动派生2::myFuncImp(int i)const->Eigen::Vector2d{
返回本征::向量2d{2*i,3*i};
}
int main(){
Matrix2Xd-m(2,3);

注:添加另一个答案,因为两个答案都有效且独立

另一种方法是定义traits类。与前面的答案相比,它的优点是,任何想要使用
Base
类型的对象的代码都不必有大量的模板参数。多个模板参数意味着它们可以混合和匹配,但这里的情况更多的是关于一个模板pe逻辑上暗示应该使用哪些类型

首先,定义一个空的traits类:

template<typename T>
class BaseTraits {};

其效果是现在已经清楚了
myFunc
应该返回什么-至少与特征的命名一样清楚;)

以下是完整的代码:

#include <Eigen/Dense>
#include <type_traits>
#include <utility>
#include <iostream>


template<typename T>
class BaseTraits {};

template<typename T>
class Base
{
public:
    using VectorType = typename BaseTraits<T>::VectorType;
    auto myFunc(int) const -> VectorType;

protected:
    Base();
};

template<typename T>
Base<T>::Base() {
    /* make sure the function is actually implemented, otherwise generate a
     * useful error message */
    static_assert( std::is_member_function_pointer_v<decltype(&T::myFuncImp)> );
}

template<typename T>
auto Base<T>::myFunc(int i) const -> VectorType {
    return static_cast<const T&>(*this).myFuncImp(i);
}


using Matrix2Xd = Eigen::Matrix<double,2,Eigen::Dynamic>;

class Derived1;

template<>
class BaseTraits<Derived1>
{
public:
    using VectorType = Matrix2Xd::ConstColXpr;
};

class Derived1 : public Base<Derived1>
{
private:
    Matrix2Xd m_data;

public:
    Derived1( Matrix2Xd&& );

private:    
    auto myFuncImp(int) const -> Matrix2Xd::ConstColXpr;
    friend Base;
};

Derived1::Derived1( Matrix2Xd&& data ) :
    m_data {data}
{}

auto Derived1::myFuncImp(int i) const -> Matrix2Xd::ConstColXpr {
    return m_data.col(i);
}

class Derived2;

template<>
class BaseTraits<Derived2>
{
public:
    using VectorType = Eigen::Vector2d;
};

class Derived2 : public Base<Derived2>
{
private:
    auto myFuncImp(int) const -> Eigen::Vector2d;
    friend Base;
};

auto Derived2::myFuncImp(int i) const -> Eigen::Vector2d {
    return Eigen::Vector2d { 2*i, 3*i };
}

int main(){
    Matrix2Xd m (2, 3);
    m <<
        0, 2, 4,
        1, 3, 5;

    Derived1 d1 { std::move(m) };

    std::cout << "d1: " << d1.myFunc(2).transpose() << "\n";

    Derived2 d2;

    std::cout << "d2: " << d2.myFunc(2).transpose() << "\n";

    return 0;
}
#包括
#包括
#包括
#包括
模板
类BaseTraits{};
模板
阶级基础
{
公众:
使用VectorType=typename BaseTraits::VectorType;
自动myFunc(int)const->VectorType;
受保护的:
Base();
};
模板
Base::Base(){
/*确保函数已实际实现,否则将生成
*有用的错误消息*/
静态断言(std::is_member_function_point)
class Derived2;

template<>
class BaseTraits<Derived2>
{
public:
    using VectorType = Eigen::Vector2d;
};
template<typename T>
class Base
{
public:
    using VectorType = typename BaseTraits<T>::VectorType;

    auto myFunc(int) const -> VectorType; /* note the speaking return type */

protected:
    Base();
};
#include <Eigen/Dense>
#include <type_traits>
#include <utility>
#include <iostream>


template<typename T>
class BaseTraits {};

template<typename T>
class Base
{
public:
    using VectorType = typename BaseTraits<T>::VectorType;
    auto myFunc(int) const -> VectorType;

protected:
    Base();
};

template<typename T>
Base<T>::Base() {
    /* make sure the function is actually implemented, otherwise generate a
     * useful error message */
    static_assert( std::is_member_function_pointer_v<decltype(&T::myFuncImp)> );
}

template<typename T>
auto Base<T>::myFunc(int i) const -> VectorType {
    return static_cast<const T&>(*this).myFuncImp(i);
}


using Matrix2Xd = Eigen::Matrix<double,2,Eigen::Dynamic>;

class Derived1;

template<>
class BaseTraits<Derived1>
{
public:
    using VectorType = Matrix2Xd::ConstColXpr;
};

class Derived1 : public Base<Derived1>
{
private:
    Matrix2Xd m_data;

public:
    Derived1( Matrix2Xd&& );

private:    
    auto myFuncImp(int) const -> Matrix2Xd::ConstColXpr;
    friend Base;
};

Derived1::Derived1( Matrix2Xd&& data ) :
    m_data {data}
{}

auto Derived1::myFuncImp(int i) const -> Matrix2Xd::ConstColXpr {
    return m_data.col(i);
}

class Derived2;

template<>
class BaseTraits<Derived2>
{
public:
    using VectorType = Eigen::Vector2d;
};

class Derived2 : public Base<Derived2>
{
private:
    auto myFuncImp(int) const -> Eigen::Vector2d;
    friend Base;
};

auto Derived2::myFuncImp(int i) const -> Eigen::Vector2d {
    return Eigen::Vector2d { 2*i, 3*i };
}

int main(){
    Matrix2Xd m (2, 3);
    m <<
        0, 2, 4,
        1, 3, 5;

    Derived1 d1 { std::move(m) };

    std::cout << "d1: " << d1.myFunc(2).transpose() << "\n";

    Derived2 d2;

    std::cout << "d2: " << d2.myFunc(2).transpose() << "\n";

    return 0;
}