在使用 C++ 这样特性丰富的语言时,警惕过度工程至关重要。当开发者为一个问题引入过于复杂或不必要的解决方案时,就发生了过度工程。
C++ 开发者可能会忍不住尽可能多地使用最近标准引入的新特性,这最终会使代码变得比实际需要的更复杂。
下面是一个例子,展示如何使用 C++ 元编程创建一个类型擦除容器,并在编译期对算术运算求值。虽然这个例子展示了 C++ 元编程技术的强大和灵活,但由于使用了模板、概念和 constexpr 函数,它可能显得复杂:
#include <iostream>
#include <type_traits>
template<typename T>
concept Arithmetic = std::is_arithmetic_v<T>;
template<Arithmetic T>
struct AnyType {
constexpr AnyType(const T& value) : value_(value) {}
template<Arithmetic U>
constexpr auto add(const AnyType<U>& other) const {
return AnyType{ value_ + other.value_ };
}
template<Arithmetic U>
constexpr auto subtract(const AnyType<U>& other) const {
return AnyType{ value_ - other.value_ };
}
template<Arithmetic U>
constexpr auto multiply(const AnyType<U>& other) const {
return AnyType{ value_ * other.value_ };
}
template<Arithmetic U>
constexpr auto divide(const AnyType<U>& other) const {
static_assert(other.value_ != 0, "Division by zero");
return AnyType{ value_ / other.value_ };
}
template<Arithmetic U>
friend std::ostream& operator<<(std::ostream& os, const AnyType<U>& any) {
return os << any.value_;
}
private:
T value_;
};
int main() {
constexpr AnyType<int> x{ 5 };
constexpr AnyType<float> y{ 2.5f };
constexpr auto addition = x.add(y);
constexpr auto subtraction = x.subtract(y);
constexpr auto multiplication = x.multiply(y);
constexpr auto division = x.divide(y);
std::cout << "Addition: " << addition << std::endl;
std::cout << "Subtraction: " << subtraction << std::endl;
std::cout << "Multiplication: " << multiplication << std::endl;
std::cout << "Division: " << division << std::endl;
return 0;
}
在这个例子中:
- 我们定义了一个概念
Arithmetic,将模板参数约束为算术类型。 AnyType类模板被定义为可以保存任何算术类型。- 我们提供了成员函数(
add、subtract、multiply、divide),用于在类型可能不同的AnyType对象之间执行算术运算。 - 我们使用
constexpr来确保这些运算在编译期求值。 - 我们重载了
operator<<,以允许将AnyType对象流式输出到std::ostream。
是的,对于某些特定需求,这样的代码可能有用。但一般来说,我们真的需要如此复杂的类来执行基本的算术运算吗?这就像为了打死一只苍蝇而造一辆坦克 :)
总而言之,避免被复杂的新特性所诱惑,只在真正需要时才使用它们。并始终努力遵循 KISS 和 YAGNI 原则:
"保持简单,别犯傻"(KISS)和"你不会需要它"(YAGNI)是倡导简单性、在真正需要之前避免不必要特性的原则。
