替换失败

时间:2012-09-05 22:10:10

标签: c++ templates c++11 sfinae typetraits

我目前正在尝试编写一些灵活的编译时数学库,并且遇到了一个我无法摆脱的替换失败。这是问题所在:

首先,我正在写一个理性的课程,我会把唯一需要的部分写下来。

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解决了这个问题,这很棒。但是,我仍然有兴趣知道替换失败的原因。

1 个答案:

答案 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问题的方法,或者是否有必要这样做。