首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

C++11 13-静态断言 Static assertions

  • 25-02-19 08:00
  • 4403
  • 9902
blog.csdn.net

系列文章目录

点击直达——文章总目录


  • C++11 13-静态断言 Static assertions
    • Overview
    • 1.静态断言 Static assertions
      • 1.1.静态断言的用途
      • 1.2.静态断言的基本语法
      • 1.3.示例代码
        • 1.3.1. 检查类型是否为整数
        • 1.3.2. 检查类型是否为指针
        • 1.3.3. 检查两个类型是否相同
        • 1.3.4. 检查常量表达式的值
        • 1.3.5. 确保类成员的存在
        • 1.3.6. 模板编程中确保参数类型符合要求
      • 1.4.注意事项
    • 2.如何使用 C++11 的 static_assert 进行模板元编程?
      • 2.1.如何使用 static_assert 进行模板元编程
    • 3.如何使用 static_assert 来检查模板参数是否满足特定的类型要求?
      • 3.1.基本语法
      • 3.2.使用 static_assert 检查模板参数类型
        • 3.2.1. 检查是否为整数类型
        • 3.2.2. 检查是否为浮点类型
        • 3.2.3. 检查是否为指针类型
        • 3.2.4. 检查是否为类类型
        • 3.2.5. 检查两个模板参数是否相同
      • 3.3.注意事项
    • 4.在模板元编程中,staticassert有哪些常见的应用场景?
    • 5.static_assert在多线程程序中如何保证其正确性?
    • 6.除了static_assert,C++11还提供了哪些编译时断言机制?
    • 7.在C++11中,如何利用’constexpr’断言来优化我的代码?
    • 8.C++20的概念(Concepts)在编译时断言中有哪些应用场景?
    • 关于作者

Overview


1.静态断言 Static assertions

在 C++11 中,static_assert 是一种编译时断言,它用于在编译时检查类型特征、表达式或条件是否为真。如果 static_assert 的条件为假,则编译器会生成一个错误。

1.1.静态断言的用途

  1. 检查类型特性:确保类型符合特定的要求,如是否是整数类型、是否是指针类型等。

  2. 确保表达式的值:检查编译时常量表达式的值是否符合预期。

  3. 模板元编程:在模板元编程中,用于确保模板参数满足特定的条件。

  4. 避免不可能的代码路径:在某些代码路径不可能被执行的情况下,使用 static_assert 来防止这些路径的出现。

1.2.静态断言的基本语法

static_assert condition, "Message";
  • 1
  • condition:一个编译时常量表达式,如果为 true,则断言成功,否则失败。
  • Message:一个字符串字面量,如果断言失败,将显示此消息。

1.3.示例代码

以下是一些使用 static_assert 的示例:

1.3.1. 检查类型是否为整数
static_assert(std::is_integral<int>::value, "int must be an integral type");
  • 1
1.3.2. 检查类型是否为指针
static_assert(std::is_pointer<int*>::value, "int* must be a pointer type");
  • 1
1.3.3. 检查两个类型是否相同
static_assert(std::is_same<int, int>::value, "Both types must be int");
  • 1
1.3.4. 检查常量表达式的值
static_assert(2 + 2 == 4, "Math is broken");
  • 1
1.3.5. 确保类成员的存在
struct MyClass {
    int value;
};

static_assert(sizeof(MyClass::value) > 0, "MyClass must have a value member");
  • 1
  • 2
  • 3
  • 4
  • 5
1.3.6. 模板编程中确保参数类型符合要求
template <typename T>
void process(T value) {
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.4.注意事项

  • 编译时检查:static_assert 是在编译时执行的,因此它不会影响程序的运行时性能。
  • 错误信息:提供明确的错误信息可以帮助调试和理解代码。
  • 滥用:不要过度使用 static_assert,因为它可能会使代码变得难以阅读和维护。

通过使用 static_assert,你可以在编译时确保代码的正确性,从而避免运行时错误,提高代码的安全性和可靠性。

2.如何使用 C++11 的 static_assert 进行模板元编程?

在 C++11 中,static_assert 是一种编译时断言,它允许你在编译时检查某个条件是否为真。如果条件为假,则编译器会生成一个错误。这在模板元编程中尤其有用,因为你可以确保模板参数满足特定的要求。

2.1.如何使用 static_assert 进行模板元编程

  1. 类型检查:
    使用 static_assert 来确保模板参数是特定的类型。例如,确保一个模板类只接受整数类型。

    template <typename T>
    struct Container {
        static_assert(std::is_integral<T>::value, "T must be an integral type.");
    };
    
    • 1
    • 2
    • 3
    • 4
  2. 常量表达式检查:
    确保某个常量表达式的值符合预期,这在定义算法或配置时非常有用。

    constexpr size_t BufferSize = 1024;
    static_assert(BufferSize % 16 == 0, "BufferSize must be a multiple of 16.");
    
    • 1
    • 2
  3. 模板参数检查:
    在模板编程中,使用 static_assert 来检查模板参数是否符合预期。

    template <typename T>
    struct MyClass {
        static_assert(std::is_floating_point<T>::value, "T must be a floating point type.");
    };
    
    • 1
    • 2
    • 3
    • 4
  4. 接口兼容性检查:
    确保派生类正确实现了基类的所有虚函数,从而保证了接口的一致性。

    class Base {
    public:
        virtual void doSomething() = 0;
    };
    class Derived : public Base {
    public:
        void doSomething() override {}
    };
    static_assert(std::is_base_of<Base, Derived>::value, "Derived must be derived from Base.");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  5. 避免重复代码:
    在多个函数或模板中使用 static_assert 来避免重复相同的类型检查或其他断言。

    template <typename T>
    void function(T param) {
        static_assert(std::is_integral<T>::value, "T must be an integral type.");
    }
    
    template <typename T>
    void anotherFunction(T param) {
        static_assert(std::is_integral<T>::value, "T must be an integral type.");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

使用 static_assert 可以在编译时发现错误,提高代码的安全性和可维护性。它特别适用于模板编程,可以确保模板参数满足特定的要求。

3.如何使用 static_assert 来检查模板参数是否满足特定的类型要求?

在 C++11 中,static_assert 是一个强大的编译时断言工具,它可以用来在编译时检查模板参数是否满足特定的类型要求。这有助于确保模板代码的类型安全性,并在编译时就发现可能的类型错误,从而避免运行时错误。

3.1.基本语法

static_assert 的基本语法如下:

static_assert(condition, "Message if condition is false");
  • 1
  • condition:一个编译时计算为布尔值的表达式。如果表达式计算结果为 true,则断言通过,否则触发编译错误。
  • Message:当条件为 false 时,显示的错误消息。

3.2.使用 static_assert 检查模板参数类型

以下是一些使用 static_assert 来检查模板参数是否满足特定类型要求的示例:

3.2.1. 检查是否为整数类型
template <typename T>
struct CheckIntegral {
    static_assert(std::is_integral<T>::value, "T must be an integral type.");
};

CheckIntegral<int> c; // 正确
CheckIntegral<double> d; // 编译错误:T must be an integral type.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.2. 检查是否为浮点类型
template <typename T>
struct CheckFloatingPoint {
    static_assert(std::is_floating_point<T>::value, "T must be a floating point type.");
};

CheckFloatingPoint<float> c; // 正确
CheckFloatingPoint<int> d; // 编译错误:T must be a floating point type.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.3. 检查是否为指针类型
template <typename T>
struct CheckPointer {
    static_assert(std::is_pointer<T>::value, "T must be a pointer type.");
};

CheckPointer<int*> c; // 正确
CheckPointer<int> d; // 编译错误:T must be a pointer type.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.4. 检查是否为类类型
template <typename T>
struct CheckClassType {
    static_assert(std::is_class<T>::value, "T must be a class type.");
};

CheckClassType<std::string> c; // 正确
CheckClassType<int> d; // 编译错误:T must be a class type.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
3.2.5. 检查两个模板参数是否相同
template <typename T, typename U>
struct CheckSameType {
    static_assert(std::is_same<T, U>::value, "T and U must be the same type.");
};

CheckSameType<int, int> c; // 正确
CheckSameType<int, double> d; // 编译错误:T and U must be the same type.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.3.注意事项

  • 编译时检查:static_assert 仅在编译时执行,不会影响程序的运行时性能。
  • 错误信息:提供明确的错误信息可以帮助调试和理解代码。
  • 滥用:不要过度使用 static_assert,因为它可能会使代码变得难以阅读和维护。

通过使用 static_assert,你可以在编译时确保代码的正确性,从而避免运行时错误,提高代码的安全性和可靠性。

4.在模板元编程中,staticassert有哪些常见的应用场景?

在模板元编程中,static_assert 是一个非常有用的工具,它允许在编译时进行条件检查。以下是一些常见的应用场景:

  1. 类型检查:
    使用 static_assert 来确保模板参数符合特定的类型要求,例如是否为整数类型、浮点类型、类类型等。

    template <typename T>
    struct IsIntegral {
        static_assert(std::is_integral<T>::value, "T must be an integral type.");
    };
    
    • 1
    • 2
    • 3
    • 4
  2. 确保常量表达式:
    检查编译时常量表达式的值是否符合预期,这在处理数组大小或数学常量时非常有用。

    constexpr int ArraySize = 42;
    static_assert(ArraySize > 0, "ArraySize must be positive.");
    
    • 1
    • 2
  3. 模板重载:
    使用 static_assert 来区分不同的模板重载,确保模板函数或类模板的参数满足特定的条件。

    template <typename T>
    void process(T value) {
        static_assert(std::is_arithmetic<T>::value, "T must be arithmetic.");
    }
    
    template <typename T>
    void process(T value, T other) {
        static_assert(!std::is_arithmetic<T>::value, "T must not be arithmetic.");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  4. 确保模板参数的兼容性:
    确保模板参数之间存在特定的关系,例如继承关系或相同的类型。

    template <typename Base, typename Derived>
    struct IsDerivedFrom {
        static_assert(std::is_base_of<Base, Derived>::value, "Derived must be derived from Base.");
    };
    
    • 1
    • 2
    • 3
    • 4
  5. 避免不可能的代码路径:
    在某些代码路径不可能被执行的情况下,使用 static_assert 来防止这些路径的出现。

    template <bool>
    struct CompileTimeCheck {
        static_assert(true, "This code path is not possible.");
    };
    
    • 1
    • 2
    • 3
    • 4
  6. 接口验证:
    确保模板参数实现了特定的接口或方法。

    template <typename T>
    struct HasMemberFunction {
        static_assert(std::is_same<decltype(T::memberFunction()), void>::value, "T must have a memberFunction.");
    };
    
    • 1
    • 2
    • 3
    • 4
  7. 配置和约束:
    在模板类或函数中,使用 static_assert 来定义编译时的配置和约束。

    template <typename T>
    struct Container {
        static_assert(sizeof(T) <= 128, "T is too large to be stored in Container.");
    };
    
    • 1
    • 2
    • 3
    • 4
  8. 模板元编程的复杂计算:
    在模板元编程中,使用 static_assert 来验证复杂的模板表达式或计算。

    template <size_t... Is>
    struct SumOfIndexes {
        static_assert(sizeof...(Is) > 0, "At least one index is required.");
        static constexpr size_t value = (0 + ... + Is);
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
  9. 文档目的:
    static_assert 可以作为文档的一部分,为模板代码提供额外的说明。

    template <typename T>
    struct MyClass {
        static_assert(!std::is_const<T>::value, "MyClass should not be used with const types.");
    };
    
    • 1
    • 2
    • 3
    • 4

通过使用 static_assert,你可以在编译时确保代码的正确性,从而避免运行时错误,提高代码的安全性和可靠性。

5.static_assert在多线程程序中如何保证其正确性?

static_assert 是 C++11 引入的一种编译时断言,用于在编译期间检查类型特性、常量表达式等是否满足特定条件。它与多线程程序的正确性没有直接关系,因为 static_assert 本身不涉及运行时的并发控制或同步机制。

static_assert 主要用于在编译时确保代码的类型安全和逻辑正确性,例如:

  1. 确保模板参数符合特定的类型要求。
  2. 检查编译时常量表达式的值是否符合预期。
  3. 验证类是否满足特定的接口或约束。

在多线程程序中,保证正确性通常需要关注以下方面:

  1. 线程安全:确保共享数据的访问是线程安全的,通常通过互斥锁、原子操作或其他同步机制实现。

  2. 死锁避免:合理设计锁的使用顺序和逻辑,避免死锁。

  3. 竞态条件:通过适当的同步措施,确保程序的执行不受竞态条件的影响。

  4. 资源管理:确保线程正确地获取和释放资源,避免资源泄露。

  5. 异常处理:确保线程中的异常得到妥善处理,不会导致资源泄露或未定义行为。

  6. 测试和验证:使用工具和技术,如动态分析器、静态分析器、压力测试等,来测试和验证多线程程序的正确性。

如果你需要在编译时检查与多线程相关的条件,可以使用 static_assert 来验证例如:

  • 确保某个类的大小适合作为线程局部存储。
  • 确保某个函数的返回类型可以安全地用于多线程环境中的原子操作。

示例:

static_assert(sizeof(MyClass) <= sizeof(std::atomic<MyClass>), "MyClass is too large for thread-local storage.");
  • 1

然而,static_assert 无法保证多线程程序在运行时的行为正确性。为了确保多线程程序的正确性,你需要关注并发编程的最佳实践和同步机制。

6.除了static_assert,C++11还提供了哪些编译时断言机制?

在 C++11 之前,编译时断言主要依靠预处理器指令 #assert,但这种方式存在一些限制,因为它不是类型安全的,并且不适用于模板编程。C++11 通过引入 static_assert 提供了一种类型安全的编译时断言机制。

除了 static_assert,C++11 还提供了以下编译时断言机制:

  1. 模板特化失败:
    通过故意使模板特化失败来达到断言的效果。如果模板实例化时编译错误,则表明条件不满足。

    template <bool> struct CompileTimeAssert {};
    template <> struct CompileTimeAssert<true> {};
    
    // Usage
    CompileTimeAssert<sizeof(int) == 4> myAssert;
    
    • 1
    • 2
    • 3
    • 4
    • 5
  2. SFINAE (Substitution Failure Is Not An Error):
    在模板重载解析中,如果模板替换失败且没有导致类型错误,则称为SFINAE。这通常与 std::enable_if 或 std::conditional 结合使用。

    template <typename T>
    using AssertIntegral = typename std::enable_if<std::is_integral<T>::value>::type;
    
    // Usage
    AssertIntegral<int> myAssert; // OK
    // AssertIntegral myAssert; // Error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  3. constexpr 断言:
    使用 constexpr 函数在编译时计算表达式,如果表达式结果为 false,则编译失败。

    constexpr bool checkCondition = true;
    static_assert(checkCondition, "Condition must be true");
    
    // Usage
    if constexpr (checkCondition) {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  4. 概念 (Concepts) (C++20):
    虽然概念是 C++20 的特性,但它们可以用来在模板编程中表达和检查约束。在 C++11 中,可以通过模板特化和 static_assert 模拟概念。

    template <typename T>
    concept bool IntegralConcept() { return std::is_integral<T>::value; }
    
    template <IntegralConcept T>
    // ...
    
    • 1
    • 2
    • 3
    • 4
    • 5
  5. 自定义类型特征:
    可以创建自定义类型特征来检查特定的条件,并在模板编程中使用它们。

    template <typename T>
    struct is_even : std::integral_constant<bool, (T % 2) == 0> {};
    
    // Usage
    static_assert(is_even<4>::value, "4 must be even");
    
    • 1
    • 2
    • 3
    • 4
    • 5
  6. 变长模板参数:
    通过匹配模板参数的数量来实现编译时断言。

    template <typename... Args>
    struct check_all_integral {
        static_assert(std::conjunction_v<std::is_integral<Args>...>, "All types must be integral.");
    };
    
    // Usage
    check_all_integral<int, short, long> myAssert; // OK
    // check_all_integral myAssert; // Error
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

static_assert 是 C++11 中最直接和最常用的编译时断言机制,但其他方法也可以在特定情况下提供有用的补充。

7.在C++11中,如何利用’constexpr’断言来优化我的代码?

在C++11中,constexpr关键字被引入,目的是提高程序的性能和效率,同时提供一种在编译时进行常量计算的方法。使用constexpr可以使得某些变量和函数的计算在编译时完成,而不是在运行时,从而减少运行时的计算负担。以下是一些使用constexpr来优化代码的方法:

  1. 定义编译时常量:使用constexpr定义的变量必须在编译时就能确定其值,这使得它们可以用于数组大小、模板参数等需要编译时常量的场合。

    constexpr int arraySize = 10;
    int myArray[arraySize]; // 使用constexpr定义的数组大小
    
    • 1
    • 2
  2. 定义常量表达式函数:constexpr函数可以返回在编译时就能确定的值,这对于执行编译时计算非常有用。

    constexpr int multiply(int x, int y) {
        return x * y;
    }
    constexpr int result = multiply(4, 5); // 在编译时计算
    
    • 1
    • 2
    • 3
    • 4
  3. 与模板结合使用:constexpr与模板结合使用,可以创建更加高效的泛型代码。模板函数和类模板的参数如果声明为constexpr,可以保证在编译时就确定其值,从而允许编译器进行更多的优化。

  4. 编译时断言:使用static_assert结合constexpr,可以在编译时检查程序的某些条件是否满足,作为一种编译时断言。

    static_assert(multiply(4, 5) == 20, "The multiplication is incorrect.");
    
    • 1
  5. 使用编译时对象:constexpr构造函数允许在编译时创建对象,这对于创建编译时已知的复杂数据结构非常有用。

    class Circle {
    public:
        constexpr Circle(int radius) : _radius(radius) {}
        constexpr double getArea() const { return _radius * _radius * 3.14159; }
    private:
        int _radius;
    };
    constexpr Circle circle(10); // 在编译时创建对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  6. 优化条件编译:从C++17开始,if constexpr允许在编译时根据条件选择执行代码块,这可以用于优化模板代码。

    template <typename T>
    constexpr auto square(T t) -> decltype(t*t) {
        if constexpr (std::is_arithmetic<T>::value) {
            return t * t;
        } else {
            // 处理非算术类型
            return t.square(); // 假设T有square成员函数
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

通过使用constexpr,你可以编写更加高效和可读的代码,并且能够利用编译器进行更多的优化。constexpr的特性和用途包括编译时常量计算、常量表达式函数、模板和泛型编程、编译时断言等。

8.C++20的概念(Concepts)在编译时断言中有哪些应用场景?

C++20中引入的概念(Concepts)是一种强大的模板编程工具,它允许开发者定义模板参数需要满足的条件,从而在编译时进行断言和类型检查。以下是一些使用Concepts进行编译时断言的应用场景:

  1. 类型检查:确保模板参数是特定类型或满足特定条件。例如,确保一个类型是整数类型或支持迭代。

    template<typename T>
    concept Integral = std::is_integral_v<T>;
    
    • 1
    • 2
  2. 函数或方法存在性检查:确保类型具有特定的成员函数。

    template<typename T>
    concept HasPrint = requires(T a) {
        { a.print() } -> std::same_as<void>;
    };
    
    • 1
    • 2
    • 3
    • 4
  3. 操作符支持检查:确保类型支持特定的操作符,如比较操作符或算术操作符。

    template<typename T, typename U>
    concept Addable = requires(T a, U b) {
        { a + b } -> std::same_as<T>;
    };
    
    • 1
    • 2
    • 3
    • 4
  4. 容器和迭代器检查:确保类型是容器并支持迭代。

    template<typename T>
    concept Iterable = requires(T c) {
        { c.begin() } -> std::forward_iterator;
        { c.end() } -> std::forward_iterator;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
  5. 函数模板重载:使用Concepts来区分模板函数的重载版本。

    template<Integral T>
    void print(T value);
    
    template<FloatingPoint T>
    void print(T value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
  6. 模板特化:使用Concepts来约束类模板或函数模板的特化。

    template<Integral T>
    class MyClass {
        // ...
    };
    
    template<>
    class MyClass<float> requires FloatingPoint<float> {
        // Specialization for floating point numbers
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  7. 编译时断言:使用static_assert结合Concepts在编译时断言类型属性。

    template<typename T>
    concept ValidType = std::is_integral<T>::value && sizeof(T) == 4;
    
    static_assert(ValidType<int>, "int is not a valid type"); // 正确
    static_assert(ValidType<long>, "long is not a valid type"); // 错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
  8. 组合Concepts:使用逻辑运算符组合多个Concepts,创建更复杂的条件。

    template<typename T>
    concept SignedIntegral = Integral<T> && std::is_signed_v<T>;
    
    template<SignedIntegral T>
    void func(T value) {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
  9. 模板参数推导:使用Concepts作为函数模板参数类型,编译器将自动推导实际类型。

    template<Iterable T>
    void printContainer(const T& container) {
        // ...
    }
    
    • 1
    • 2
    • 3
    • 4
  10. 编译时错误信息:当模板参数不满足Concepts时,编译器可以提供清晰的错误信息。

使用Concepts可以提高代码的可读性和安全性,同时在编译时发现错误,避免运行时问题。这些应用场景展示了Concepts在实际编程中的灵活性和强大功能。


关于作者

  • 微信公众号:WeSiGJ
  • GitHub:https://github.com/wesigj/cplusplusboys
  • CSDN:https://blog.csdn.net/wesigj
  • 微博:
  • -版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
WeSiGJ
微信公众号
共同分享,共同交流, 共同学习!
注:本文转载自blog.csdn.net的WeSiGJ的文章"https://wesigj.blog.csdn.net/article/details/143028585"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2491) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top