博客 阅读时间 2 分钟

C++ 现在是一门特性丰富的语言:谨防过度工程

分享本文
C++ Is Now a Feature-Rich Language: Beware of Overengineering

在使用 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类模板被定义为可以保存任何算术类型。
  • 我们提供了成员函数(addsubtractmultiplydivide),用于在类型可能不同的AnyType对象之间执行算术运算。
  • 我们使用constexpr来确保这些运算在编译期求值。
  • 我们重载了operator<<,以允许将AnyType对象流式输出到std::ostream

是的,对于某些特定需求,这样的代码可能有用。但一般来说,我们真的需要如此复杂的类来执行基本的算术运算吗?这就像为了打死一只苍蝇而造一辆坦克 :)

总而言之,避免被复杂的新特性所诱惑,只在真正需要时才使用它们。并始终努力遵循 KISS 和 YAGNI 原则:

"保持简单,别犯傻"(KISS)和"你不会需要它"(YAGNI)是倡导简单性、在真正需要之前避免不必要特性的原则。

分享本文