我目前正在尝试编写一些灵活的编译时数学库,并且遇到了一个我无法摆脱的替换失败。这是问题所在:
首先,我正在写一个理性的课程,我会把唯一需要的部分写下来。
template<typename T>
class rational
{
static_assert(std::is_integral<T>::value, "Can only contain integral values.");
public:
constexpr rational(T numerator, T denominator);
private:
T _numerator;
T _denominator;
};
为了让库变得灵活,我试图大量使用SFINAE,以便将操作符函数调用仅限于理性 - 理性,理性 - 积分和积分 - 理性,但无论积分是什么,它都可以工作而积分的基本类型是。以下是operator+
的函数声明,例如:
template<typename T, typename U>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const rational<T>& lhs, const rational<U>& rhs);
template<typename T, typename U>
constexpr
typename std::enable_if<std::is_integral<U>::value, rational<typename std::common_type<T, U>::type>>::type
operator+(const rational<T>& lhs, const U& rhs);
template<typename T, typename U>
constexpr
typename std::enable_if<std::is_integral<T>::value, rational<typename std::common_type<T, U>::type>>::type
operator+(const T& lhs, const rational<U> rhs);
这是一段错误的代码。它不会因static_assert
而崩溃,但可能是因为替换失败:
constexpr auto r1 = rational<int>(1, 2);
constexpr auto r2 = rational<int>(2, 4);
static_assert(r1 + r2 == rational<int>(1, 1), "");
错误如下(我只保留错误,没有周围的blabla):
... required by substitution of 'template<class T, class U> constexpr typename std::enable_if<std::is_integral<T>::value, smath::rational<typename std::common_type<_Tp, _Up>::type> >::type smath::operator+(const T&, smath::rational<U>) [with T = smath::rational<int>; U = int]'
... required from here
... error: operands to ?: have different types 'smath::rational<int>' and 'int'
... required by substitution of 'template<class T, class U> constexpr typename std::enable_if<std::is_integral<U>::value, smath::rational<typename std::common_type<_Tp, _Up>::type> >::type smath::operator+(const smath::rational<T>&, const U&) [with T = int; U = smath::rational<int>]'
... required from here
... error: operands to ?: have different types 'int' and 'smath::rational<int>'
我的猜测是g ++会选择第一个与两个有理数一起使用的模板函数,并且可以使用它。但是,它似乎仍然尝试应用最后两个函数,并在尝试这样做时失败。我无法理解。一些帮助将受到欢迎:)
编辑:似乎让rational
构造函数explicit
解决了这个问题,这很棒。但是,我仍然有兴趣知道替换失败的原因。
答案 0 :(得分:1)
问题是传递给std::enable_if<whatever, T>
的类型。即使替换失败,论证也需要合理,但事实并非如此。因此,如果潜在评估类型没有此类型,则typename std::common_type<T, U>::type
的使用不起作用。你还需要别的东西。有效的是创建替换失败,禁用模板参数列表中的混合整数/有理重载:
template<typename T, typename U, typename = typename std::enable_if<std::is_integral<U>::value, void>::type>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const rational<T>& lhs, const U& rhs);
template<typename T, typename U, typename = typename std::enable_if<std::is_integral<T>::value, void>::type>
constexpr
rational<typename std::common_type<T, U>::type>
operator+(const T& lhs, const rational<U> rhs);
现在我不完全确定这是否是解决gcc问题的方法,或者是否有必要这样做。