泛型运算符重载,多个抽象类c++

问题描述 投票:0回答:1

我正在创建一个使用Dimension3作为抽象类的库。

例如:

/// \brief Abstract class to represent 3 dimensions
/// \note This class is not meant to be used directly, but to be inherited by other classes
/// \details This class implements the basic operations for 3 dimensions (+, +=, -, -=, *, *=, /, /=, %, %=, ==, !=, <, >, <=, >=, -, ++, --, <<)
class dimension3 {
protected:
    template<typename T>
    using enable_if_dimension3 = std::enable_if_t<std::is_base_of_v<dimension3, T>>;

public:
    double x{}, y{}, z{};
    
    /// \brief Add a double to a dimension3 (dim3 += n)
    /// \tparam T Type of the dimension3, must be a derived class of dimension3
    /// \param dimension3 Dimension3 containing the result
    /// \param n Value to add
    /// \return A reference to the dimension3 with the result of the addition
    template<typename T, typename = enable_if_dimension3<T>>
    friend const T& operator+=(T& dimension3, double n) noexcept {
        dimension3.x += n;
        dimension3.y += n;
        dimension3.z += n;
        return dimension3;
    }
    virtual void something_virtual() = 0;
}; // class dimension3

这工作得很好,我可以实现

vector3 : public dimension3

但是,由于明显的原因,当我想添加

dimension4
时,我无法使用相同的布局:

/// \brief Abstract class to represent 4 dimensions
/// \note This class is not meant to be used directly, but to be inherited by other classes
/// \details This class implements the basic operations for 4 dimensions (+, +=, -, -=, *, *=, /, /=, %, %=, ==, !=, <, >, <=, >=, -, ++, --, <<)
class dimension4 {
protected:
    template<typename T>
    using enable_if_dimension4 = std::enable_if_t<std::is_base_of_v<dimension4, T>>;

public:
    double x{}, y{}, z{}, w{};
    
    /// \brief Add a double to a dimension4 (dim4 += n)
    /// \tparam T Type of the dimension4, must be a derived class of dimension4
    /// \param dimension4 Dimension4 containing the result
    /// \param n Value to add
    /// \return A reference to the dimension4 with the result of the addition
    template<typename T, typename = enable_if_dimension4<T>>
    friend const T& operator+=(T& dimension4, double n) noexcept
    {
        dimension4.x += n;
        dimension4.y += n;
        dimension4.z += n;
        dimension4.w += n;
        return dimension4;
    }
    virtual void something_virtual() = 0;
}; // class dimension4

这是行不通的,因为它不能定义

friend const T& operator+=(T&, double)
两次。这个问题有一些简单的解决方案吗? 我见过人们使用
decltype()
dimension3<T>
,但我不太喜欢第二个选项。

最小可行产品

class point3 : public dimension3 {
public:
    point3(double x, double y, double z) {
        this->x = x;
        this->y = y;
        this->z = z;
    }
    void something_virtual() override {}
}; // class point3

class point4 : public dimension4 {
public:
    point4(double x, double y, double z, double w)
    {
        this->x = x;
        this->y = y;
        this->z = z;
        this->w = w;
    }
    void something_virtual() override {}
}; // class point4

int main(int argc, char* argv[])
{
    point3 p3{1, 2, 3};
    point4 p4{1, 2, 3, 4};

    p3 += 1.2;
    p4 += 1.2; // error: No viable operator+= matches arguments of type point4 and double.
}

我想这不一定是关于重载的问题,而是更多关于泛型的问题。

c++ generics
1个回答
0
投票

问题是你选择了错误的方式来实现SFINAE。

正如编译器所说:https://godbolt.org/z/re7zE6K55

<source>:46:37: error: template parameter redefines default argument
   46 |     template<typename T, typename = enable_if_dimension4<T>>
      |                                     ^
<source>:20:37: note: previous default template argument defined here
   20 |     template<typename T, typename = enable_if_dimension3<T>>
      |                                     ^
<source>:47:21: error: redefinition of 'operator+='
   47 |     friend const T& operator+=(T& dimension4, double n) noexcept
      |                     ^

所以问题是模板的默认类型参数不明确

operator+=

将模板更改为单一类型参数,并通过将其应用于返回类型来实现 SFINAE,然后您的代码开始编译:

/// \brief Abstract class to represent 3 dimensions
/// \note This class is not meant to be used directly, but to be inherited by other classes
/// \details This class implements the basic operations for 3 dimensions (+, +=, -, -=, *, *=, /, /=, %, %=, ==, !=, <, >, <=, >=, -, ++, --, <<)
class dimension3 {
protected:
    template<typename T>
    using enable_if_dimension3 = std::enable_if_t<std::is_base_of_v<dimension3, T>, const T&>;

public:
    double x{}, y{}, z{};
    
    /// \brief Add a double to a dimension3 (dim3 += n)
    /// \tparam T Type of the dimension3, must be a derived class of dimension3
    /// \param dimension3 Dimension3 containing the result
    /// \param n Value to add
    /// \return A reference to the dimension3 with the result of the addition
    template<typename T>
    friend auto operator+=(T& dimension3, double n) noexcept -> enable_if_dimension3<T> {
        dimension3.x += n;
        dimension3.y += n;
        dimension3.z += n;
        return dimension3;
    }
    virtual void something_virtual() = 0;
}; // class dimension3

https://godbolt.org/z/EnhEf8aaq

免责声明:
这是一个快速修复。我修复后最好重命名相关符号。

最后一点:
使用 C++20,您可以使用概念并使之变得更好。

© www.soinside.com 2019 - 2024. All rights reserved.