多语言模式表示方法及C++代码重复模式优化咨询
Great question! Dealing with boilerplate code where logic repeats but targets different members is a common pain point. Let's walk through how to clean up your C++ code first, then look at how other languages handle this same pattern.
C++ Optimization Solutions
1. Member Pointers + Generic Helper Function
The most straightforward type-safe approach is to extract the core logic into a helper function that takes a pointer to the class member container. This way, you only write the check logic once:
#include <vector> #include <algorithm> class layerclass { public: std::vector<int> a; std::vector<int> b; std::vector<int> c; private: // Generic check that works with any of our vector members bool isIn(const std::vector<int> layerclass::*container, int x) const { return std::find((this->*container).begin(), (this->*container).end(), x) != (this->*container).end(); } public: bool isInA(int x) const { return isIn(&layerclass::a, x); } bool isInB(int x) const { return isIn(&layerclass::b, x); } bool isInC(int x) const { return isIn(&layerclass::c, x); } }; class innerlayer : public layerclass { public: layerclass* outerlayer; private: bool isIn(const std::vector<int> layerclass::*container, int x) const { if (layerclass::isIn(container, x)) { return true; } return outerlayer->isIn(container, x); } public: bool isInA(int x) const { return isIn(&layerclass::a, x); } bool isInB(int x) const { return isIn(&layerclass::b, x); } bool isInC(int x) const { return isIn(&layerclass::c, x); } };
Now you only need to update the isIn helper if your logic changes—no more copying and pasting the same check three times.
2. C++17 Auto Template Parameters (Advanced)
For a more modern, concise approach, use C++17's auto template parameters to bind directly to member pointers at compile time:
#include <vector> #include <algorithm> class layerclass { public: std::vector<int> a; std::vector<int> b; std::vector<int> c; template<auto ContainerPtr> bool isIn(int x) const { return std::find((this->*ContainerPtr).begin(), (this->*ContainerPtr).end(), x) != (this->*ContainerPtr).end(); } // Convenience wrappers for external use bool isInA(int x) const { return isIn<&layerclass::a>(x); } bool isInB(int x) const { return isIn<&layerclass::b>(x); } bool isInC(int x) const { return isIn<&layerclass::c>(x); } }; class innerlayer : public layerclass { public: layerclass* outerlayer; template<auto ContainerPtr> bool isIn(int x) const { if (layerclass::isIn<ContainerPtr>(x)) { return true; } return outerlayer->isIn<ContainerPtr>(x); } bool isInA(int x) const { return isIn<&layerclass::a>(x); } bool isInB(int x) const { return isIn<&layerclass::b>(x); } bool isInC(int x) const { return isIn<&layerclass::c>(x); } };
This approach is fully type-safe and avoids runtime overhead, since the template is resolved at compile time.
3. Macros (Quick but Not Recommended)
If you need a fast fix, macros can generate the repetitive functions, but they come with downsides like poor readability and debugging difficulty. Use this sparingly:
#include <vector> #include <algorithm> #define DEFINE_IS_IN(NAME) \ bool isIn##NAME(int x) const { \ return std::find(NAME.begin(), NAME.end(), x) != NAME.end(); \ } #define DEFINE_INNER_IS_IN(NAME) \ bool isIn##NAME(int x) const { \ if (layerclass::isIn##NAME(x)) return true; \ return outerlayer->isIn##NAME(x); \ } class layerclass { public: std::vector<int> a; std::vector<int> b; std::vector<int> c; DEFINE_IS_IN(a) DEFINE_IS_IN(b) DEFINE_IS_IN(c) }; class innerlayer : public layerclass { public: layerclass* outerlayer; DEFINE_INNER_IS_IN(a) DEFINE_INNER_IS_IN(b) DEFINE_INNER_IS_IN(c) };
How Other Languages Handle This Pattern
Python
Python's dynamic nature lets you generate methods on the fly or use attribute access to avoid repetition:
class LayerClass: def __init__(self): self.a = [] self.b = [] self.c = [] def _is_in(self, container_name, x): return x in getattr(self, container_name) # Dynamically create is_in_a, is_in_b, is_in_c for name in ['a', 'b', 'c']: def make_method(name): def method(self, x): return self._is_in(name, x) return method setattr(LayerClass, f"is_in_{name}", make_method(name)) class InnerLayer(LayerClass): def __init__(self, outer_layer): super().__init__() self.outer_layer = outer_layer def _is_in(self, container_name, x): if super()._is_in(container_name, x): return True return self.outer_layer._is_in(container_name, x) # Generate the same methods for InnerLayer for name in ['a', 'b', 'c']: def make_method(name): def method(self, x): return self._is_in(name, x) return method setattr(InnerLayer, f"is_in_{name}", make_method(name))
Java
Java uses suppliers or method references to pass the target container, with type safety enforced at compile time:
import java.util.ArrayList; import java.util.List; import java.util.Objects; import java.util.function.Supplier; class LayerClass { private List<Integer> a = new ArrayList<>(); private List<Integer> b = new ArrayList<>(); private List<Integer> c = new ArrayList<>(); private boolean isIn(Supplier<List<Integer>> containerSupplier, int x) { return containerSupplier.get().contains(x); } public boolean isInA(int x) { return isIn(() -> a, x); } public boolean isInB(int x) { return isIn(() -> b, x); } public boolean isInC(int x) { return isIn(() -> c, x); } // Getters needed for InnerLayer access protected List<Integer> getA() { return a; } protected List<Integer> getB() { return b; } protected List<Integer> getC() { return c; } } class InnerLayer extends LayerClass { private LayerClass outerLayer; public InnerLayer(LayerClass outerLayer) { this.outerLayer = Objects.requireNonNull(outerLayer); } private boolean isIn(Supplier<List<Integer>> containerSupplier, int x) { if (super.isIn(containerSupplier, x)) { return true; } return outerLayer.isIn(containerSupplier, x); } @Override public boolean isInA(int x) { return isIn(() -> getA(), x); } @Override public boolean isInB(int x) { return isIn(() -> getB(), x); } @Override public boolean isInC(int x) { return isIn(() -> getC(), x); } }
TypeScript/JavaScript
Dynamic property access combined with type safety (in TypeScript) makes this pattern easy to handle:
class LayerClass { a: number[] = []; b: number[] = []; c: number[] = []; private _isIn(containerName: keyof LayerClass, x: number): boolean { return this[containerName].includes(x); } isInA(x: number): boolean { return this._isIn('a', x); } isInB(x: number): boolean { return this._isIn('b', x); } isInC(x: number): boolean { return this._isIn('c', x); } } class InnerLayer extends LayerClass { outerLayer: LayerClass; constructor(outerLayer: LayerClass) { super(); this.outerLayer = outerLayer; } private _isIn(containerName: keyof LayerClass, x: number): boolean { if (super._isIn(containerName, x)) { return true; } return this.outerLayer._isIn(containerName, x); } isInA(x: number): boolean { return this._isIn('a', x); } isInB(x: number): boolean { return this._isIn('b', x); } isInC(x: number): boolean { return this._isIn('c', x); } }
Key Takeaway
Across all languages, the core strategy is the same: extract the reusable logic into a single function, and pass the varying part (like the target container) as a parameter. Static languages lean on type-safe mechanisms like member pointers or suppliers, while dynamic languages use more flexible attribute access and method generation.
内容的提问来源于stack exchange,提问作者user2183336

