编译期构造函数选择:GeoJSON position_t API实现技术问询
实现编译期构造的
geojson::position_t<>解决方案 要实现你需要的多种构造方式,我们可以结合C的编译期重载解析、constexpr构造函数和类型约束(SFINAE或C20 Concepts)来完成,同时用std::variant让position_t<>能灵活存储点、线、多边形三种几何类型。下面是具体的实现方案:
核心实现思路
我们让position_t<>内部用std::variant存储不同的几何数据结构,然后为每种构造场景编写对应的constexpr构造函数,通过类型约束确保编译器在编译期就能选择正确的构造逻辑,避免重载冲突。
完整代码实现
#include <variant> #include <vector> #include <utility> #include <type_traits> namespace geojson { template <typename Coord = double> class position_t { private: // 定义三种几何类型的内部存储 using Point = std::pair<Coord, Coord>; using LineString = std::vector<position_t<Coord>>; using Polygon = std::vector<LineString>; std::variant<Point, LineString, Polygon> data_; public: // 1. 构造点:直接传入两个坐标值 constexpr position_t(Coord x, Coord y) noexcept : data_(Point{std::move(x), std::move(y)}) {} // 2. 构造点:传入std::pair(支持任意可转换为Coord的pair类型) template <typename X, typename Y> requires std::is_convertible_v<X, Coord> && std::is_convertible_v<Y, Coord> constexpr position_t(const std::pair<X, Y>& p) noexcept : data_(Point{static_cast<Coord>(p.first), static_cast<Coord>(p.second)}) {} // 完美转发版本,支持右值pair template <typename X, typename Y> requires std::is_convertible_v<X, Coord> && std::is_convertible_v<Y, Coord> constexpr position_t(std::pair<X, Y>&& p) noexcept : data_(Point{static_cast<Coord>(std::move(p.first)), static_cast<Coord>(std::move(p.second))}) {} // 3. 构造线:传入多个position_t的初始化列表 constexpr position_t(std::initializer_list<position_t<Coord>> points) noexcept : data_(LineString{points.begin(), points.end()}) {} // 4. 构造多边形:传入嵌套的初始化列表(多个线环) constexpr position_t(std::initializer_list<std::initializer_list<position_t<Coord>>> rings) noexcept { Polygon poly; poly.reserve(rings.size()); for (const auto& ring : rings) { poly.emplace_back(ring.begin(), ring.end()); } data_ = std::move(poly); } // 辅助方法:判断当前存储的几何类型 [[nodiscard]] constexpr bool is_point() const noexcept { return std::holds_alternative<Point>(data_); } [[nodiscard]] constexpr bool is_line_string() const noexcept { return std::holds_alternative<LineString>(data_); } [[nodiscard]] constexpr bool is_polygon() const noexcept { return std::holds_alternative<Polygon>(data_); } // 辅助方法:获取对应类型的数据(需确保当前类型匹配,否则会抛出std::bad_variant_access) template <typename T> [[nodiscard]] constexpr const T& get() const noexcept { return std::get<T>(data_); } template <typename T> [[nodiscard]] constexpr T& get() noexcept { return std::get<T>(data_); } }; } // namespace geojson
代码解释与编译期逻辑
构造点的两种方式:
- 直接传两个坐标值的构造函数会被
point_1{10,12}和point_2(11,13)匹配,编译期就能完成点的构造。 - 针对
std::pair的构造函数用C20的requires约束(如果你用C17及以下,可以替换为std::enable_if),确保只有当传入的pair元素能转换为Coord类型时才启用,这样point_3(std::pair<int, int>(12,14))就能正确匹配并构造点。
- 直接传两个坐标值的构造函数会被
构造线与多边形:
- 接受
std::initializer_list<position_t<Coord>>的构造函数会被line_1{ ... }匹配,自动将多个点组合成线。 - 接受嵌套初始化列表的构造函数会被
poly_1{ { ... }, { ... } }匹配,将多个线环组合成多边形,这一步也是编译期完成的(只要初始化列表是编译期常量)。
- 接受
编译期安全:
所有构造函数都是constexpr的,只要传入的参数是编译期常量,整个对象的构造过程会在编译期完成,完全符合你的编译期构造需求。同时,类型约束确保编译器不会在重载解析时出错,每个构造场景都有明确的匹配逻辑。
测试你的示例代码
现在你给出的示例代码可以直接编译运行:
int main() { // 点构造 geojson::position_t<> point_1{ 10, 12 }; geojson::position_t<> point_2( 11, 13 ); geojson::position_t<> point_3(std::pair<int, int>(12, 14)); // 线构造 geojson::position_t<> line_1{ geojson::position_t<> { 100, 120 }, geojson::position_t<> { 110, 130 } }; // 多边形构造 geojson::position_t<> poly_1{ { geojson::position_t<> { 100, 120 }, geojson::position_t<> { 110, 130 }}, { geojson::position_t<> { 101, 121 }, geojson::position_t<> { 111, 131 }} }; // 验证类型(可选) static_assert(point_1.is_point()); static_assert(line_1.is_line_string()); static_assert(poly_1.is_polygon()); return 0; }
兼容C++17的调整
如果你需要兼容C++17,把requires替换为std::enable_if即可,比如将pair的构造函数改成:
template <typename X, typename Y, typename = std::enable_if_t<std::is_convertible_v<X, Coord> && std::is_convertible_v<Y, Coord>>> constexpr position_t(const std::pair<X, Y>& p) noexcept : data_(Point{static_cast<Coord>(p.first), static_cast<Coord>(p.second)}) {}
内容的提问来源于stack exchange,提问作者drus
相关产品推荐
相关产品推荐

