C++ 模板返回类型取决于模板参数
c++ template return type depending on template arguments?
我已经实现了自己的SI单位类。使用算术运算时,生成的 SI 单位可能会发生变化。例如:(米/秒)/米=1/秒。
好的,现在我还创建了一个简单的 3D 矢量类。这个向量应该是通用的,也可以与我的 SI 单位类一起使用。所以我实现了一个简单的除法运算符。请参阅以下代码:
// Determine result type of Lhs / Rhs:
template < class Lhs, class Rhs >
struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };
// Vector / Vector:
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
const RobotTools::DataTypes::TV3Type< Rhs >& rhs )
{
// do something useful
return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type >( 0, 0, 0 );
}
// Vector / Vector
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
vl / vl; // Ok this works
在编译期间,将使用 TV3TypeV3Div 结构确定正确的返回类型。这行得通。
现在我想扩展运算符。我还想用标量类型计算向量。所以我写了这个运算符:
// Vector / Scalar
template < class Lhs, class Rhs >
RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Rhs >::type > operator/( const RobotTools::DataTypes::TV3Type< Lhs >& lhs,
const Rhs& rhs )
{
// do something useful
return RobotTools::DataTypes::TV3Type< typename TV3TypeV3Div< Lhs, Tools::DataTypes::Length >::type >( 0, 0, 0 );
}
// Vector / Scalar
RobotTools::DataTypes::TV3Type< Tools::DataTypes::Length > vl;
Tools::DataTypes::Length sl;
vl / sl; // Ok nice it works too
目前为止,一切都好。问题是当我定义第二个运算符(矢量/标量)时,这个运算符是如此通用,以至于编译器也想将其用于矢量/矢量除法。但它失败了,因为 Lhs( )/Rhs( ) 具有:
lhs=Tools::D ataTypes::Length and Rhs=RobotTools::D ataTypes::TV3Type
未定义。这是正确的,我理解给定的错误。我不明白的是编译器不使用矢量/矢量运算符。
- 是否有可能给编译器一个提示使用哪个运算符?
- 是否有可能重写运算符以满足我的要求?
编译器不希望使用 Vector/Scalar 运算符进行 Vector/Vector 除法。它只是想检查是否有匹配项。
如果您声明(但不定义)完全泛型除法运算符
template <typename Lhs, typename Rhs>
struct InvalidDivision {};
template <typename Lhs, typename Rhs>
InvalidDivision<Lhs, Rhs>
operator/(const Lhs& lhs, const Rhs& rhs); // do not define
然后,您的代码应该编译并链接。矢量/标量重载将被考虑并拒绝,因为矢量/矢量是更好的匹配。
它的缺点是,如果你确实划分了没有为它们定义除法的东西,编译器会抱怨InvalidDivision<something,other>
而不是给出它通常的错误。
也许这可以通过使用SFINAE或其他一些高级魔法来改善。
更新:更详细的解释
代码的原始版本是怎么回事?
我们正在尝试为用户定义类型调用除法运算符。有两个名为 operator/
的函数,编译器会考虑这两个函数,并尝试找出哪个更匹配。
首先考虑矢量/矢量operator/
。编译器推断Lhs
和Rhs
模板参数都是Length
的(为了简洁起见,我省略了命名空间)。然后它将它们代入TV3TypeV3Div
的参数,计算TV3TypeV3Div
的内部,确定TV3TypeV3Div<Lhs,Rhs>::type
也是Length
的,最后计算operator/
的返回类型,即TV3Type<Length>
。
现在考虑矢量/标量operator/
。编译器推断Lhs
是Length
,但Rhs
是TV3Type<Length>
。然后它把这些类型代入TV3TypeV3Div
的参数,并尝试计算TV3TypeV3Div
的内部。事情在这里破裂:没有operator/
会接受Length
和TV3Type<Length>
。所以不可能计算TV3TypeV3Div<Lhs,Rhs>::type
.编译器输出错误。
现在考虑声明泛型operator/
时会发生什么情况。现在有一只operator/
可以接受Length
和TV3Type<Length>
!(或任何其他论点,就此而言)。因此,编译器很乐意计算TV3TypeV3Div<Lhs,Rhs>::type
,然后计算向量/标量operator/
的返回类型。因此,现在可以同时考虑矢量/矢量和矢量/标量operator/
重载。
编译器现在还查看并考虑重载解析的泛型operator/
。所有三个重载都会产生匹配项。但是向量/向量重载胜出,因为它比向量/标量或泛型operator/
更专业,因此是更好的匹配。
更新 2
应该可以这样做:
namespace Detail
{
template <typename Lhs, typename Rhs>
struct DoNotUse {};
template <typename Lhs, typename Rhs>
DoNotUse<Lhs, Rhs> operator/(const Lhs& lhs, const Rhs& rhs);
template < class Lhs, class Rhs >
struct TV3TypeV3Div { typedef BOOST_TYPEOF( Lhs( ) / Rhs( ) ) type; };
}
using Detail::TV3TypeV3Div;
这应该很好地向除TV3TypeV3Div
以外的所有人隐藏泛型运算符/。
- 将强制转换简化为取决于参数的类型
- cpp 模板专用化,错误说参数 1 的类型为 T,这取决于参数 T
- 模板函数,其中模板参数类型取决于函数参数
- GCC:指定的界限取决于源参数的长度
- 具有尾随返回类型的通用 lambda,具体取决于 C++11 中的可变参数
- 类模板中的std ::数组的大小,具体取决于模板参数
- LIBELAS是否取决于相机参数
- 具有取决于模板参数的方法的模板类
- 模板层次结构中的可选虚拟函数,具体取决于参数
- 约束模板参数取决于传递的函子
- 为什么固定尺寸错误发生取决于默认参数
- C++14 lambda 的默认参数类型推导,具体取决于前面的参数
- 委托构造函数和默认参数,具体取决于其他参数
- 取决于参数的数量,将其返回结构对象,并将其分配给常规结构的对象
- 具有模板参数取决于参数列表
- 返回类型取决于模板参数
- 是否可以定义一个 C++11 可变参数类模板,其可变参数基数取决于整数模板参数
- 返回取决于sizeof的变量类型..参数包
- 模板化结构的友元函数,其参数类型取决于结构的内部
- 变量模板函数,其中返回类型取决于模板参数列表