为什么我的重载乘法运算符没有被识别?

3
为什么以下代码会产生编译错误 no match for operator*
template<class E>
class vector_expression {};

template<class Tuple>
class vector
    : public vector_expression<vector<Tuple>>
{
public:
    using value_type = typename Tuple::value_type;
};

namespace detail
{
    template<typename T>
    class scalar
        : public vector_expression<scalar<T>>
    {};
}

template<class E1, class E2, class BinaryOperation>
class vector_binary_operation
    : public vector_expression<vector_binary_operation<E1, E2, BinaryOperation>>
{
public:
    template<class F1, class F2>
    vector_binary_operation(F1&& e1, F2&& e2, BinaryOperation op)
        : m_e1(std::forward<F1>(e1)), m_e2(std::forward<F2>(e2)),
          m_op(std::move(op))
    { }

private:
    E1 m_e1;
    E2 m_e2;
    BinaryOperation m_op;
};

template<class E>
vector_binary_operation<detail::scalar<typename E::value_type>, E, std::multiplies<>> operator*(typename E::value_type value, E&& e) {
    return { std::move(value), std::forward<E>(e), std::multiplies<>{} };
}
template<class E>
vector_binary_operation<E, detail::scalar<typename E::value_type>, std::multiplies<>> operator*(E&& e, typename E::value_type value) {
    return { std::forward<E>(e), std::move(value), std::multiplies<>{} };
}

int main()
{
    vector<std::array<double, 3>> x;
    3 * x;

    return 0;
}

DEMO


2
你的“DEMO”链接对我来说无法使用。 - Martin Bonner supports Monica
2
请注意,3int 类型,而不是 double 类型。模板对此类细节非常敏感。 - Cornstalks
@MartinBonner 抱歉,已修复。 - 0xbadf00d
@Cornstalks 这不是重点。如果你用 3. 替换 3,它仍然无法工作。 - 0xbadf00d
我认为我只需要使用 typename std::decay_t<E>::value_type 而不是 typename E::value_type - 0xbadf00d
@Cornstalks:那是我的第一个猜测——但是没有 :-( - Martin Bonner supports Monica
2个回答

3

你有两个重载的operator*,它们(暂不考虑返回类型):

template <class E>
R operator*(typename E::value_type, E&& );

template <class E>
R operator*(E&&, typename E::value_type );

在这两种情况下,一个参数是非推导上下文。我们从第二个重载开始。当我们使用3 * x调用时,E被推导为int,没有int::value_type,因此会有替换失败。
在第一个重载中,我们将E推导为vector<std::array<double, 3>>&。请注意,它是一个引用。因此,没有E::value_type,因为它是一个引用类型。你必须先删除该部分(对于这两个重载)。最简单的方法是引入第二个默认的模板参数,它是E引用版本。
template<class E, class ER = std::remove_reference_t<E>>
vector_binary_operation<detail::scalar<typename ER::value_type>, ER, std::multiplies<>>
operator*(typename ER::value_type value, E&& e);

有了这个修复,现在你的代码因为一个不同的原因不能编译:scalar没有构造函数。但这是一个无关的问题。

“decay” 打起来更短(而且它执行的额外转换是无害的) - T.C.
你说得对,我刚刚注意到我忘记写 std::decay_t<E> 而不是 E - 0xbadf00d
@T.C. 这个更短,但有时我更喜欢使用 remove_reference,因为它更清晰地表明我们只是在移除一个引用。 - Barry

1
代码失败是因为x是一个左值引用,你不能在其上应用::访问运算符。为了做到这一点,你应该先对推导出的类型E进行std::decay_t处理,即写成
typename std::decay_t<E>::value_type

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接