【发布时间】:2020-04-14 22:52:28
【问题描述】:
注意:
在解释和我的示例中,我使用的是 eigen 库。但是,我的问题可能会被不熟悉该库的人概括和理解,例如将ConstColXpr 替换为std::string_view,将Vector 替换为std::string。
问题: 我想使用 CRTP 创建一个接口,其中有两个继承自它的类,它们在调用某些成员函数时有以下不同:
- 第一个类返回数据成员的视图(
Eigen::Matrix<...>::ConstColXpr) - 第二个类没有这个数据成员。而是在调用函数时计算适当的值,然后返回(作为
Eigen::Vector<...>)
两种返回类型具有相同的维度(例如 2x1 列向量)和相同的接口,即可以以完全相同的方式进行交互。这就是为什么我认为将函数定义为接口的一部分是合理的。
但是,我不知道如何正确定义/限制基类/接口中的返回类型。 auto 可以正常编译和执行,但不会告诉用户任何期望。
是否可以以更清晰的方式定义接口?我尝试将std::invoke_result与实现功能一起使用,但我必须在接口之前包含继承类型,即相当落后。而且它并不比auto 好多少,因为仍然需要在实现中查找实际类型。
一个很好的答案是常见的Eigen 类型,其中尺寸清晰。但是,我不希望调用接口函数需要模板参数(我必须与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;
}
在我的机器上,打印出来
d1: 4 5
d2: 4 6
【问题讨论】:
-
你能做一些愚蠢的事情吗,比如返回一个继承自
ConstColXpr的类型,但可以选择包含Vector,然后ConstColXpr会引用它? coliru.stacked-crooked.com/a/5c8e0d58dd204d2f -
我实际上只是继续使用占位符类型(尽管
decltype(auto)可能更好),并在 cmets 和其他任何适合T::myFuncImp返回的类型的要求的地方记录,这也是Base<T>::myFunc返回的类型的保证。这允许一些新的派生类以后使用一些全新的类型,只要它具有适当的接口。 -
Eigen::ConstColExpr和Eigen::Vector2d没有表达其维度的通用基类。您可以静态断言您的返回类型的RowAtCompileTime是 2(和 Cols==1)。而且,返回Vector2d甚至可能比返回对列的引用更有效(至少在 x86-64 上,Vector2d只是通过xmm0寄存器传递) -
例如,如果某个派生类有
myFuncImpl返回类型const Thing&,那么decltype(auto)会有myFunc也有返回类型const Thing&,但只有auto会有@987654355 @返回类型Thing,强制复制。 -
en.cppreference.com/w/cpp/language/auto 涵盖了它。基本上
decltype(auto)遵循与decltype(expression)相同的规则,并且任何其他占位符类型使用与模板参数推导相同的规则找到auto的类型,这意味着auto本身就像推导函数参数一样输入T为f(T),auto&的作用类似于f(T&)中的参数等。
标签: c++ templates eigen return-type crtp