【发布时间】:2021-05-31 15:14:27
【问题描述】:
我正在开发一个库,该库使用名为 PETE 的非常古老的 C++ 表达式模板 (ET) 引擎。 (我试图找到它的源代码的链接,以便我可以引用它,但我只找到了关于它的文章。)
快速概述:使用 ET,C++ 编译器使用操作符中缀形式(+、-、*、/)从表达式构建,这是一种表示表达式及其操作的 C++ 类型。 PETE 方法的核心是 ForEach 类模板,用于稍后解析和评估表达式。
我想做的是提供一个专门的ForEach,当它的参数满足特定条件时使用它。我正在尝试使用部分专业化并使用enable_if,但编译器抱怨“不明确的部分专业化”。
如果需要,我很乐意发布代码的其他部分,但我会坚持使用有问题的直接类模板(注意:我添加了 Enable 参数,以便使用 @987654325 选择后面的专业@. NB2:为了更短的帖子,我不包括方法的实现):
template<class Expr, class FTag, class CTag, class Enable = void>
struct ForEach
{
typedef typename LeafFunctor<Expr, FTag>::Type_t Type_t;
inline static
Type_t apply(const Expr &expr, const FTag &f, const CTag &)
{
// empty
}
};
然后是第一个部分专业化(也是标准 PETE 的一部分)。这就是后来所说的数字“1”:
// 1
template<class Op, class A, class B, class FTag, class CTag>
struct ForEach<BinaryNode<Op, A, B>, FTag, CTag >
{
typedef typename ForEach<A, FTag, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, FTag, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, Op, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<Op, A, B> &expr, const FTag &f,
const CTag &c)
{
// default implementation for BinaryNode
}
};
这是编译器抱怨的我的附加部分专业化。它实际上抱怨数字“2”与数字“1”模棱两可:
// A
template<class A, class B, class CTag>
struct ForEach<BinaryNode<OpMultiply, A, B>, ViewSpinLeaf, CTag , enable_if_t< ! EvalToSpinMatrix<A>::value > >
{
typedef typename ForEach<A, ViewSpinLeaf, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, ViewSpinLeaf, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, OpMultiply, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<OpMultiply, A, B> &expr, const ViewSpinLeaf &f,
const CTag &c)
{
// default implementation for BinaryNode (this is the same as above)
}
};
// 2
template<class A, class B, class CTag>
struct ForEach<BinaryNode<OpMultiply, A, B>, ViewSpinLeaf, CTag , enable_if_t< EvalToSpinMatrix<A>::value > >
{
typedef typename ForEach<A, ViewSpinLeaf, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, ViewSpinLeaf, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, OpMultiply, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<OpMultiply, A, B> &expr, const ViewSpinLeaf &f, const CTag &c)
{
// special implementation for when EvalToSpinMatrix<A>::value is true
}
};
编译器错误如下(注意:我重新格式化以增强可读性)
ambiguous template instantiation for ‘struct ForEach<BinaryNode<OpMultiply, Vector<double>, Vector<double> >, ViewSpinLeaf, OpCombine, void>’
candidate '1':
candidates are: template<class Op, class A, class B, class FTag, class CTag> struct ForEach<BinaryNode<Op, A, B>, FTag, CTag> ;
Op = OpMultiply;
A = Vector<double>;
B = Vector<double>;
FTag = ViewSpinLeaf;
CTag = OpCombine;
candidate '2':
note: template<class A, class B, class CTag> struct ForEach<BinaryNode<OpMultiply, T1, T2>, ViewSpinLeaf, CTag, typename std::enable_if<EvalToSpinMatrix<A>::value, void>::type>;
A = Vector<double>;
B = Vector<double>;
CTag = OpCombine;
据我了解,该标准应用了所谓的“部分排序”,它表示如果一个部分专业化与另一个专业化程度最低,但反之则不然。应用于这个例子是这样说的:
数字 2 至少与数字 1 一样专业,因为对于每个参数集(对于数字 2),我可以找到一个匹配的集合(对于数字 1)。但是数字 1 至少不像数字 2 那样专业。如果我将 FTag 设置为除 ViewSpinLeaf 之外的任何值,那么数字 2 就无法匹配。因此,数字 2 更加专业。所以,我不明白为什么编译器不这么看。
作为第二次测试,我删除了专业化“A”(带有否定 enable_if 的那个)并从专业化“2”中删除了 enable_if_t 位。这编译得很好,这意味着数字“2”内的所有其他语句/类型定义都可以工作。但是,这不是我所需要的,因为此代码路径适用于所有 BinaryNode<OpMultiply,..> 而不仅仅是特定情况。
以防万一。我使用的编译器是 Ubuntu 上的 g++ 9.3,启用了标准 C++14。
编辑:正如评论中所建议的那样,BinaryNode<Op,..> 和 BinaryNode<OpMultiply,..> 之间可能存在歧义。我将数字“2”更改为以下内容:
// 2
template<class Op, class A, class B, class CTag>
struct ForEach<BinaryNode<Op, A, B>, ViewSpinLeaf, CTag , enable_if_t< EvalToSpinMatrix<A>::value > >
{
typedef typename ForEach<A, ViewSpinLeaf, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, ViewSpinLeaf, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, Op, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<OpMultiply, A, B> &expr, const ViewSpinLeaf &f, const CTag &c)
{
}
inline static
Type_t apply(const BinaryNode<Op, A, B> &expr, const ViewSpinLeaf &f, const CTag &c)
{
}
};
现在只有FTag 更专业。编译器抱怨同样的歧义:
note: candidates are: ‘template<class Op, class A, class B, class FTag, class CTag> struct ForEach<BinaryNode<Op, A, B>, FTag, CTag>;
Op = OpMultiply;
A = Vector<double>;
B = Vector<double>;
FTag = ViewSpinLeaf;
CTag = OpCombine;
‘template<class Op, class A, class B, class CTag> struct ForEach<BinaryNode<Op, A, B>, ViewSpinLeaf, CTag, typename std::enable_if<EvalToSpinMatrix<A>::value, void>::type>;
Op = OpMultiply;
A = Vector<double>;
B = Vector<double>;
CTag = OpCombine;
数字“2”显然更专业。
EDIT2:添加一个最小的复制器。有一个#if 0,如果这样,程序将编译并采用默认代码路径。但是,当使用#if 1 打开部分特化时,就会重现歧义。
#include<type_traits>
#include<iostream>
using namespace std;
template<class T> class Vector {};
struct ViewSpinLeaf {};
struct OpCombine {};
template<class LeafType, class LeafTag> struct LeafFunctor {};
template<class A, class B, class Op, class Tag> struct Combine2 {};
template<class T1, class T2, class Op>
struct BinaryReturn {
typedef T1 Type_t;
};
template<class T>
struct LeafFunctor<Vector<T>, ViewSpinLeaf>
{
typedef T Type_t;
inline static
Type_t apply(const Vector<T> & s, const ViewSpinLeaf& v)
{
return Type_t();
}
};
template<class A,class B,class Op>
struct Combine2<A, B, Op, OpCombine>
{
typedef typename BinaryReturn<A, B, Op>::Type_t Type_t;
inline static
Type_t combine(const A& a, const B& b, const Op& op, const OpCombine& do_not_use)
{
return op(a, b);
}
};
struct OpMultiply
{
template<class T1, class T2>
inline typename BinaryReturn<T1, T2, OpMultiply >::Type_t
operator()(const T1 &a, const T2 &b) const
{
return (a * b);
}
};
template<class Op, class Left, class Right>
class BinaryNode
{
public:
BinaryNode(const Op &o, const Left &l, const Right &r) : op_m(o), left_m(l), right_m(r) {}
private:
Op op_m;
Left left_m;
Right right_m;
};
template<class Expr, class FTag, class CTag, class Enable = void >
struct ForEach
{
typedef typename LeafFunctor<Expr, FTag>::Type_t Type_t;
inline static
Type_t apply(const Expr &expr, const FTag &f, const CTag &)
{
return LeafFunctor<Expr, FTag>::apply(expr, f);
}
};
template<class Expr, class FTag, class CTag>
inline typename ForEach<Expr,FTag,CTag>::Type_t
forEach(const Expr &e, const FTag &f, const CTag &c)
{
return ForEach<Expr, FTag, CTag>::apply(e, f, c);
}
template<class Op, class A, class B, class FTag, class CTag>
struct ForEach<BinaryNode<Op, A, B>, FTag, CTag >
{
typedef typename ForEach<A, FTag, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, FTag, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, Op, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<Op, A, B> &expr, const FTag &f,
const CTag &c)
{
std::cout << "I don't want to be here. " << std::endl;
return Type_t();
}
};
#if 0
template<class A>
struct EvalToSpinMatrix
{
constexpr static bool value = false;
};
template<>
struct EvalToSpinMatrix<Vector<double> >
{
constexpr static bool value = true;
};
template<class A, class B, class CTag>
struct ForEach<BinaryNode<OpMultiply, A, B>, ViewSpinLeaf, CTag , enable_if_t< EvalToSpinMatrix<A>::value > >
{
typedef typename ForEach<A, ViewSpinLeaf, CTag>::Type_t TypeA_t;
typedef typename ForEach<B, ViewSpinLeaf, CTag>::Type_t TypeB_t;
typedef typename Combine2<TypeA_t, TypeB_t, OpMultiply, CTag>::Type_t Type_t;
inline static
Type_t apply(const BinaryNode<OpMultiply, A, B> &expr, const ViewSpinLeaf &f, const CTag &c)
{
std::cout << "I want to get here. " << std::endl;
return Type_t();
}
};
#endif
int main(int argc, char **argv)
{
OpMultiply op;
Vector<double> left;
Vector<double> right;
BinaryNode< OpMultiply , Vector<double> , Vector<double> > rhs(op,left,right);
forEach( rhs , ViewSpinLeaf() , OpCombine() );
}
我应该说基于EvalToSpinMatrix trait 选择带有enable_if 开关的部分特化很重要。显然,在实际应用中,这个特征更加复杂。很好,它在这个简单的版本中重现了歧义。
【问题讨论】:
-
似乎歧义不在
BinaryNode<>之间,而是在BinaryNode<Op, A, B>及其专业化BinaryNode<OpMultiply, A, B>之间。您的 #1 与您的专业没有相同数量的参数。 -
@xryl669 有道理。我测试了一个在那个地方不应该有歧义的版本。尽管如此,编译器并不认为数字 2 更专业。
-
@ritter,对答案提供一些反馈怎么样?
标签: c++ template-meta-programming sfinae