我正在创建一个使用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.
}
我想这不一定是关于重载的问题,而是更多关于泛型的问题。
问题是你选择了错误的方式来实现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,您可以使用概念并使之变得更好。