问题描述
我有一个受openfoam强烈影响的有限体积库,该库使解决连续力学问题的解决方案与用C ++编写类似 将在纸上。例如,要解决不可压缩的层流的Navier-Stokes方程,我只需编写:
solve(div(U * U) - nu * lap(U) == -grad(p));
当然,此表达式包含表达式模板,以便单次计算系统系数,并以无矩阵方式求解系统。
那些表达式模板基于CRTP,因此所有必需的运算符都在基类中定义。特别是,等于运算符的定义如下:
template <typename T>
class BaseExpr {};
template <std::size_t dim,std::size_t rank,typename... TRest,template <std::size_t,std::size_t,typename...> typename T>
class BaseExpr<T<dim,rank,TRest...>>
{
// ...
public:
template <typename... URest,typename...> typename U>
SystemExpr<dim,T<dim,TRest...>,U<dim,URest...>>
operator ==(BaseExpr<U<dim,URest...>> const &rhs) const
{ return {*this,rhs}; }
SystemExpr<dim,Tensor<dim,rank>>
operator ==(Tensor<dim,rank> const &rhs) const
{ return {*this,rhs}; }
};
其中SystemExpr<dim,URest...>>
具有延迟评估系数和计算解的功能。
与OpenFOAM相反,在OpenFOAM中,您必须限定fvm::lap
,fvc::grad
等,以表明该术语将被明确评估或
隐式地,我采用了我一直在纸上使用的约定:左侧的所有内容都隐式评估,而右侧的所有内容都隐式评估
被明确评估。因此,BaseExpr::operator ==
不是可交换的。随着方程变得更长,这变得越来越有用。例如,可压缩流的ε传输方程为:
solve(div(φ * ε) - div(µt * grad(ε)) / σε + ρ * Sp((C2 * ω + 2.0 / 3.0 * C1 * div(U)) * ε) == C1 * ω * G);
我担心在C ++ 20下此设计可能会由于operator ==
的新“重写候选”而中断。 G ++-10会在没有任何警告的情况下编译库
与-std=c++20 -Wall -Wextra -pedantic
一起使用,但我想再次检查:以上代码在C ++ 20下格式是否正确?
我知道有些人可能认为上面的设计很糟糕,但是我喜欢它,我宁愿停留在-std=c++17
模式而不是使用
另一个表示相等的运算符(例如operator >>
或其他)(是的,这是相等的一种形式)。
使用GCC-10.2,我得到了想要的行为。考虑非稳态传热方程:
solve(d(T) / dt - α * lap(T) == 0); // OK: implicit scheme
solve(d(T) / dt == α * lap(T)); // OK: explicit scheme
solve(d(T) / dt - 0.5 * α * lap(T) == 0.5 * α * lap(T)); // OK: Crank-Nicholson scheme
solve(0 == d(T) / dt - α * lap(T)); // OK: doesn't compile
最后一个示例不编译是完全可以的,因为它没有意义。我得到的错误是:
prova.cpp:51:11: error: return type of ‘VF::TExprSistema<d,r1,T<d,TRest ...>,VF::TTensor<d,r> > VF::TExprBase<T<d,TRest ...> >::operator==(const VF::TTensor<d,r>&) const [with long unsigned int d = 3; long unsigned int r1 = 0; TRest = {VF::TExprBinaria<3,VF::TExprd<3,0>,double,std::divides<void> >,VF::TExprBinaria<3,VF::TExprLap<3,void>,std::multiplies<void> >,std::minus<void>}; T = VF::TExprBinaria]’ is not ‘bool’
51 | solve(0 == d(T) / dt - α * lap(T));
| ~~^~~~~~~~~~~~~~~~~~~~~~~~~
prova.cpp:51:11: note: used as rewritten candidate for comparison of ‘int’ and ‘VF::TExprBinaria<3,std::minus<void> >’
因此,即使在C ++ 20模式下,GCC的表现也完全符合我的要求。
这是一个“最小”示例:
#include <cstddef>
#include <utility>
#include <functional>
template<typename>
class TExprBase;
template<std::size_t,typename,typename>
class TExprSistema;
template<std::size_t,typename>
class TExprUnaria;
template<std::size_t,typename>
class TExprBinaria;
template<std::size_t,std::size_t>
class TCampo;
template<typename T>
class TExprBase {};
template<std::size_t d,std::size_t r1,template<std::size_t,typename...> typename T>
class TExprBase<T<d,TRest...>>
{
public:
template<typename... URest,typename...> typename U>
TExprBinaria<d,U<d,URest...>,std::plus<>>
operator +(TExprBase<U<d,rhs}; }
template<typename... URest,std::minus<>>
operator -(TExprBase<U<d,rhs}; }
TExprUnaria<d,std::negate<>>
operator -() const
{ return {*this}; }
template<std::size_t r2,typename... URest,r1 + r2,r2,std::multiplies<>>
operator *(TExprBase<U<d,rhs}; }
TExprBinaria<d,std::multiplies<>>
operator *(double const rhs) const
{ return {*this,0u,std::divides<>>
operator /(TExprBase<U<d,std::divides<>>
operator /(double const rhs) const
{ return {*this,typename...> typename U>
TExprSistema<d,URest...>>
operator ==(TExprBase<U<d,rhs}; }
operator T<d,TRest...> const &() const
{ return *static_cast<T<d,TRest...> const *>(this); }
};
template<std::size_t d,std::size_t r,typename T,typename U>
class TExprSistema : public TExprBase<TExprSistema<d,r,T,U>>
{
private:
TExprBase<T> const &lhs;
TExprBase<U> const &rhs;
public:
TExprSistema() = delete;
TExprSistema(TExprBase<T> const &lhs_,TExprBase<U> const &rhs_) :
lhs(lhs_),rhs(rhs_) {}
TExprSistema(TExprBase<T> const &lhs_,U const &rhs_) :
lhs(lhs_),rhs(rhs_) {}
};
template<std::size_t d,typename TOp>
class TExprUnaria : public TExprBase<TExprUnaria<d,TOp>>
{
private:
T const &rhs;
[[no_unique_address]] TOp const Op = {};
public:
TExprUnaria(T const &rhs_) :
rhs(rhs_) {}
};
template<std::size_t d,typename U,typename TOp>
class TExprBinaria : public TExprBase<TExprBinaria<d,U,TOp>>
{
private:
T const &lhs;
U const &rhs;
[[no_unique_address]] TOp const Op = {};
public:
TExprBinaria(T const &lhs_,std::size_t r>
class TCampo : public TExprBase<TCampo<d,r>> {};
template<std::size_t d,typename T>
class TExprDiv : public TExprBase<TExprDiv<d,T>> {};
template<std::size_t d,std::size_t r>
class TExprGrad : public TExprBase<TExprGrad<d,typename T>
class TExprLap : public TExprBase<TExprLap<d,typename...> typename T>
TExprDiv<d,r - 1u,1u,TRest...>>
inline div(TExprBinaria<d,TCampo<d,r - 1u>,std::multiplies<>> const &)
{ return {}; }
template<std::size_t d,std::size_t r>
TExprGrad<d,r + 1u>
inline grad(TCampo<d,r> const &)
{ return {}; }
template<std::size_t d,std::size_t r>
TExprLap<d,void>
inline lap(TCampo<d,std::size_t r>
class TSistema
{
public:
template<typename T,typename U>
TSistema(TExprSistema<d,U> const &);
void
Solve() const;
void
friend solve(TSistema const &Sistema)
{ Sistema.solve(); }
};
template<std::size_t d,typename U>
void
inline solve(TExprSistema<d,U> const &Expr)
{ solve(TSistema(Expr)); }
int main()
{
TCampo<3u,1u> U;
TCampo<3u,0u> nu,p;
solve(div(U * U) - nu * lap(U) == -grad(p));
return 0;
}
解决方法
我将严格按照以下步骤简化您的示例:
template <typename T>
struct Other { };
template <typename T>
struct Base {
template <typename U>
void operator==(Base<U> const&) const;
template <typename U>
void operator==(Other<U> const&) const;
};
struct A : Base<A> { };
template <typename T> struct B : Base<B<T>> { };
基本上,您拥有的是一些CRTP基类模板,该模板可与任何其他类似的东西以及任何Other<U>
媲美。我为此选择了最简单的非bool
返回类型,即... void
。
问题就变成了:上述工作有效吗?比较A
,B<T>
和Other<U>
的每一种组合都行得通吗?
答案是:它可能会满足您的要求。
将A{}
与B<int>{}
或B<double>{}
与B<char>{}
进行比较。在这种情况下,我们既有真实的候选者(从左手边)也有重写的候选者(从右手边),并且这两个候选者都涉及到两者 em>参数,因此首选非重写候选者。
即使我们直接在任一方向上将Base<K>
与A
比较,也是如此。这是一个对称比较。
其他类型更有趣。
在该方向上将A{}
与Other<T>{}
进行比较是可以的。我们像在C ++ 17中那样调用成员operator==
,这是唯一的候选者,并且它执行您期望的工作。
在C ++ 17中,由于Other<T>{}
与A{}
的比较(即,在另一个方向)比较不正确,因为没有任何候选。但是由于不同的原因,它在C ++ 20中格式错误。现在我们有一个候选:反向A{} == Other<T>{}
候选。但是由于[over.match.oper]/9,我们最终拒绝了候选人:
如果通过重载决议为
operator==
选择了重写的operator @
候选者,则其返回类型应为cvbool
,并且[...]
请注意,bool
要求的返回类型在过载解析的 end 中起作用(如果非返回bool
的重写候选者获胜,那是不正确的-而不是开始处(如您在评论中所暗示的那样,暗示不返回bool
的函数甚至不被视为重写候选对象)。
因此,我们确实考虑了该候选者(这是我们唯一的候选者),但是由于它是无效的重写候选者,因此我们拒绝了。结果,它在C ++ 20中仍然像在C ++ 17中那样格式错误。由于其他原因,它现在的格式不正确,因此您将收到不同的错误消息。例如,C给我:
error: return type 'void' of selected 'operator==' function for rewritten '==' comparison is not 'bool'
b == a;
~ ^ ~
而在C ++ 17中它将给出:
error: invalid operands to binary expression ('Other<int>' and 'A')
b == a;
~ ^ ~