问题描述
我使用以下代码来比较算术类型。类模板 equal
比较给定的参数是否相等并将结果存储在成员变量 result
中。类模板还为 bool
提供了一个强制转换运算符,以便在条件语句中进行评估。我还提供了模板推导指南,以便在构造 equal
对象时不必手动转换参数。当 equal
从非模板化函数中求值时,这始终有效,但当表达式被 operator!()
否定时,无法在函数模板中编译,至少在当前的 clang 编译器 (11.0.1) 上是这样。但是,它使用 gcc 和 msvc 进行编译。在代码示例中,函数模板dummy()
中的编译失败:
#include <algorithm>
#include <concepts>
#include <limits>
#include <type_traits>
template<typename T>
inline constexpr auto abs(T value) -> T
{
if (value < 0)
return -1 * value;
return value;
}
template<std::integral T>
inline constexpr auto integersEqual(const T lref,const T rref) noexcept -> bool
{
return lref == rref;
}
template<std::floating_point T>
inline constexpr auto floatingPointsEqual(const T lref,const T rref) noexcept -> bool
{
constexpr T epsilon = std::numeric_limits<T>::epsilon() * 1000;
return abs(lref - rref) <=
(epsilon * std::max<T>(abs(lref),abs(rref)));
}
template<typename T>
inline constexpr auto arithmeticEqual(const T lref,const T rref) noexcept -> bool
{
if constexpr (std::is_integral_v<T>)
{
return integersEqual(lref,rref);
}
else
{
return floatingPointsEqual(lref,rref);
}
}
template<typename T>
class equal final
{
public:
inline constexpr equal(const T lref,const T rref) noexcept : result(arithmeticEqual(lref,rref)) {}
inline constexpr operator bool() const noexcept
{
return result;
}
inline constexpr auto operator!() const noexcept -> bool
{
return !result;
}
private:
const bool result;
};
// deduction guides for equal
template<typename T1,typename T2>
equal(T1,T2) -> equal<std::common_type_t<std::decay_t<T1>,std::decay_t<T2>>>;
template<typename T1,typename T2>
auto dummy(const T1 t1,const T2 t2) -> bool
{
return !equal(t1,t2); // <- This line fails. If not negated,it works
//return equal(t1,t2) == false; // this works
//return !equal<std::common_type_t<std::decay_t<T1>,std::decay_t<T2>>>(t1,t2); // this works as well
}
auto main() -> int
{
return dummy(12,13);
}
(链接到编译器资源管理器:https://godbolt.org/z/PMcMab)
<source>:67:12: error: invalid argument type 'equal' to unary expression
return !equal(t1,t2);
将 operator!()
添加到类模板 equal
并不能解决此问题。我的问题是:为什么 clang 不接受这个代码?我看不出这段代码有问题。这是clang中的错误吗?通常 clang 比其他编译器更严格地遵循标准,所以我想知道 gcc 和 msvc 是否都接受此代码但真的不应该。我怀疑模板参数推导是罪魁祸首,因为提供具体的模板参数可以解决问题。扣除是否由于某种我不知道的原因而失败?
解决方法
我有点认为这是 clang 中的一个错误。我摆弄了一段时间。
我试过了:
return equal( t1,t2 ).operator !();
哪个 clang 不喜欢,但其他编译器喜欢。
Clang 说:
<source>:69:27: error: member reference base type 'equal' is not a structure or union
return equal( t1,t2 ).operator !();
一个有趣的错误。
如果你只是这样做:
bool dummy2( int t1,unsigned t2)
{
return !equal(t1,t2);
}
它编译得很好。所以它与推导指南结合在模板方法中调用推导指南有关。
最后编译:
template<typename T1,typename T2>
auto dummy(const T1 t1,const T2 t2) -> bool
{
typedef decltype( equal(t1,t2) ) _TyEqual;
static_assert( std::is_same_v< _TyEqual,equal< std::common_type_t<std::decay_t<T1>,std::decay_t<T2>>>> );
return !_TyEqual(t1,t2);
}
再加上其他错误,我认为错误是在应用演绎指南时,clang 使用了某种中间类型。一旦你把类型拼出来,它就会喜欢它......
无论如何,有趣的问题,我在这个过程中获得了一些关于演绎指南的知识。