重载一组类的加号运算符

Overloading the plus operator for a set of classes

本文关键字:运算符 一组 重载      更新时间:2023-10-16

请考虑以下代码片段:

template<class E>
class vector_expression
{};
template<class Tuple>
class vector
    : public vector_expression<vector<Tuple>>
{};
template<class E1, class E2, class BinaryOperation>
class vector_binary_operation
    : public vector_expression<vector_binary_operation<E1, E2, BinaryOperation>>
{
public:
    vector_binary_operation(E1&& e1, E2&& e2, BinaryOperation op)
        : m_e1(e1), m_e2(e2),
          m_op(op)
    {}
private:
    E1 m_e1;
    E2 m_e2;
    BinaryOperation m_op;
};
template<class E1, class E2>
vector_binary_operation<E1, E2, std::plus<>> operator+(E1&& e1, E2&& e2) {
    return{ std::forward<E1>(e1), std::forward<E2>(e2), std::plus<>{} };
}

上面的代码确保vector_binary_operation存储对命名对象的引用,并为临时对象制作副本。问题出在operator+的接口上,因为它实际上为任何类型的运算符定义了这个运算符。如果我想保留功能,但只为vector_expression或派生的类型定义运算符,我需要更改什么?

您可以

通过以下方式使用 SFINAE 和 std::is_base_of

template<class E1, class E2>
std::enable_if_t<std::is_base_of_v<vector_expression<std::decay_t<E1>>, std::decay_t<E1>>&&
                 std::is_base_of_v<vector_expression<std::decay_t<E2>>, std::decay_t<E2>>,
                 vector_binary_operation<std::decay_t<E1>, std::decay_t<E2>, std::plus<>>>
operator+(E1&& e1, E2&& e2) {
    return {
             std::forward<std::decay_t<E1>>(e1),
             std::forward<std::decay_t<E2>>(e2),
             std::plus<>{}
           };
}

现场演示

@

101010已经给出了原理。当您在我的一个答案的评论中也提出这个问题时,我正在扩展它。

我猜你重载的运算符比operator+多,编写一个 traits 类来检查是否所有传递的类型都vector_expression s,这很方便:

template<typename ... Ts> struct is_vector_expression 
     : public std::false_type {};
template<typename T> struct is_vector_expression<T>
     : public std::is_base_of<vector_expression<std::decay_t<T> >, std::decay_t<T> >::type {};
template<typename T, typename ... Ts> struct is_vector_expression<T, Ts ...>
     : public std::integral_constant<bool, is_vector_expression<T>::value
                                        && is_vector_expression<Ts ...>::value> {};

使用 C++17 时,您还可以跳过可变参数模板的内容并使用 std::conjunction .

接下来,您可以将其包装在合适的别名中,这样您就不必一直编写std::enable_if_t

template<typename ... Ts>
using enable_for_vector_expression = std::enable_if_t<is_vector_expression<Ts...>::value>;

最后,您可以像以下示例中对所有重载运算符一样使用它:

template<typename E1, typename E2,
         typename = enable_for_vector_expression<E1, E2> >
auto operator+(E1&& e1, E2&& e2)
{
    //...
}