0

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

首先,我正在写一个理性类,我会放唯一需要的部分。

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

4

1 回答 1

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

于 2012-09-05T22:50:45.773 回答