(C++17) any的使用与简单实现

需求

在C++这种静态强类型语言中,在一些开发场景下有时需要一种万能类型。

这之后std::any就应运而生了,any 描述用于任何类型的单个值的类型安全容器。

使用

std::any - cppreference.com

注意std::any不是一个模板类,但内部实现基本都是模板

ref示例

其实这个示例已经将大多数功能都有所演示了。

#include <any>
#include <iostream>

int main() {
    std::cout << std::boolalpha;

    // any 类型
    std::any a = 1;
    std::cout << a.type().name() << ": " << std::any_cast<int>(a) << '\n';
    a = 3.14;
    std::cout << a.type().name() << ": " << std::any_cast<double>(a) << '\n';
    a = true;
    std::cout << a.type().name() << ": " << std::any_cast<bool>(a) << '\n';

    // 有误的转型
    try {
        a = 1;
        std::cout << std::any_cast<float>(a) << '\n';
    } catch (const std::bad_any_cast& e) {
        std::cout << e.what() << '\n';
    }

    // 拥有值
    a = 1;
    if (a.has_value()) {
        std::cout << a.type().name() << '\n';
    }

    // 重置
    a.reset();
    if (!a.has_value()) {
        std::cout << "no value\n";
    }

    // 指向所含数据的指针
    a      = 1;
    int* i = std::any_cast<int>(&a);
    std::cout << *i << "\n";
}

构造

std::make_any

与大多数make工程函数一样

#include <any>
#include <iostream>
#include <string>

int main() {
    auto anyy = std::make_any<std::string>(3, 'a');
    std::cout << anyy.type().name() << std::endl;
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
}

emplace

#include <any>
#include <iostream>
#include <string>

int main() {
    std::any anyy = std::string("123");
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
    
    anyy.emplace<std::string>(3, 'a');
    std::cout << std::any_cast<std::string>(anyy) << std::endl;
}

访问

std::any_cast<>

std::any_cast<>是很大的一个讲究。因为涉及各种数据类型问题。

普通对象转换

当检测到数据类型错误时,会抛出异常bad any_cast

#include <any>
#include <iostream>
#include <string>

int main() {
    auto a = std::any(12);
    std::cout << std::any_cast<int>(a) << '\n';

    try {
        std::cout << std::any_cast<std::string>(a) << '\n';
    } catch (const std::bad_any_cast& e) {
        // bad any_cast
        std::cout << e.what() << '\n';
    }
}
指针转换
#include <any>
#include <iostream>
#include <string>
#include <utility>

void show(std::any& a) {
    // 指针示例
    // 转换失败为 nullptr
    // 成功为 内部实际存储对象地址
    if (int* i = std::any_cast<int>(&a)) {
        std::cout << "ptr = " << i << " val = " << *i << std::endl;
    } else if (std::string* s = std::any_cast<std::string>(&a)) {
        std::cout << "ptr = " << s << " val = " << *s << std::endl;
    } else {
        std::cout << "The anyObj is not a int or string\n";
    }
}

int main() {
    // 简单示例
    auto a = std::any(12);
    std::cout << "ptr = " << &a << std::endl;
    show(a);

    a.emplace<std::string>(std::string("Hello World"));
    std::cout << "ptr = " << &a << std::endl;
    show(a);
}

某次的测试结果是这样的。

可见内是对对象进行了重新的构造。

ptr = 0x61fe98
ptr = 0x61fe9c val = 12
ptr = 0x61fe98
ptr = 0x8f7a20 val = Hello World
引用转换

自行注意各种应用在使用的时候的区别。

注意转为右值引用时候注意所有权的问题。

#include <any>
#include <iostream>
#include <string>

int main() {
    auto a = std::any(std::string("hello"));
    std::cout << std::any_cast<std::string>(a) << std::endl;

    // T&
    auto& ref = std::any_cast<std::string&>(a);
    ref[0]    = 'H';
    std::cout << std::any_cast<std::string>(a) << std::endl;

    // const T&
    const auto& cref = std::any_cast<const std::string&>(a);
    // error
    // cref[0] = 'W';

    // T&&
    // 这里加不加 std::move() 效果都一样
    auto&& b = std::any_cast<std::string&&>(std::move(a));
    std::cout << "ptr = " << &b << " val = " << b << std::endl;
    std::cout << "ptr = " << std::any_cast<std::string>(&a)
              << " val = " << *std::any_cast<std::string>(&a) << std::endl;
}

逻辑判断

因为有没有operator bool()所以不可以直接在if等逻辑判断中使用。

#include <any>

int main() {
    // 简单示例
    auto a = std::any(12);
    std::cout << std::any_cast<int>(a) << '\n';

    if (a.has_value()) {
    }
    // 没有 operator bool()
    // if (a) {}
}

⭐手动实现

借助模板技术,我们可以手动实现一个Any。

首先很使用智能指针可以帮我们减少很多对内存问题的负担。

然后主要是能够做到不同数据类型的切换,普通的方法肯定是不行的。

好在C++中有模板类的技术,通过继承的方式,达到类型擦除的效果。

#include <iostream>
#include <memory>

class Any {
private:
    template <typename _Ptr>
    using SmartPtr = std::unique_ptr<_Ptr>;

private:  // 通过继承的方式达到类型擦除
    struct Value {
        virtual SmartPtr<Value> clone() = 0;
        virtual ~Value() {
        }
    };

    template <typename T>
    struct ValueHolder : Value {
        T data;
        ValueHolder(T t) : data(t) {
        }

        /**
         * !!! 注意,这里使用 std::unique_ptr的话
         * 不能直接`return new ValueHolder<T>(data)`
         * 自己手写的智能指针,没有这个问题
         * =======================================
         * error: could not convert
         * '(operator new(8), (<statement>,
         * ((Any::ValueHolder<int>*)<anonymous>)))' from
         * 'Any::ValueHolder<int>*'
         * to
         * 'Any::SmartPtr<Any::Value> {
         *      aka std::unique_ptr<Any::Value, std::default_delete<Any::Value>
         * >
         * }'
         */
        virtual SmartPtr<Value> clone() override {
            // return new ValueHolder<T>(data)
            return SmartPtr<Value>{new ValueHolder<T>(data)};
        }
    };

private:
    SmartPtr<Value> m_value;

public:
    template <typename T>
    Any(T t) : m_value{new ValueHolder<T>(t)} {
    }

    Any(const Any &rhs) {
        this->operator=(rhs);
    }

    Any &operator=(const Any &rhs) {
        m_value = rhs.m_value->clone();
        return *this;
    }

public:
    // 这里是否返回引用,均可(看调用的最终目的)
    template <typename T>
    T &value() const {
        // 查看是否能够进行类型转换
        if (auto p = dynamic_cast<ValueHolder<T> *>(m_value.get())) {
            return p->data;
        }
        // 类型转换失败则抛出异常
        throw std::logic_error("value() => Type Error");
    }
};

/**
 * 检测传参的拷贝操作
 */
void show(Any a) {
}

int main() {
    Any anyy = 1;
    show(anyy);

    try {
        anyy.value<int>() += 1;
        int inter = anyy.value<int>();
        std::cout << typeid(inter).name() << " " << inter << std::endl;

        anyy     = "abc";
        // 这里存的是 const char*
        auto str = anyy.value<std::string>();
        std::cout << typeid(str).name() << " " << str << std::endl;
    } catch (const std::exception &e) {
        std::cerr << e.what() << '\n';
    }
}

参考:

【C++ STL】C++17 实用 std::any 存储任意类型_哔哩哔哩_bilibili

std::any 详细使用方法 - 哔哩哔哩 (bilibili.com)




END

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单使用C++实现socket连接的示例代码,其中实现了客户端和服务器端之间的基本通信。 服务器端代码: ```c++ #include <iostream> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <cstring> using namespace std; int main() { // 创建socket int server_socket = socket(AF_INET, SOCK_STREAM, 0); if (server_socket == -1) { cerr << "创建socket失败" << endl; return -1; } cout << "socket创建成功" << endl; // 绑定IP地址和端口号 struct sockaddr_in server_addr{}; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_port = htons(12345); if (bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) { cerr << "绑定IP地址和端口号失败" << endl; close(server_socket); return -1; } cout << "绑定IP地址和端口号成功" << endl; // 监听连接请求 if (listen(server_socket, 10) == -1) { cerr << "监听连接请求失败" << endl; close(server_socket); return -1; } cout << "等待客户端连接..." << endl; // 接受连接请求 struct sockaddr_in client_addr{}; socklen_t client_addr_len = sizeof(client_addr); int client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &client_addr_len); if (client_socket == -1) { cerr << "接受连接请求失败" << endl; close(server_socket); return -1; } cout << "客户端连接成功" << endl; // 接收客户端发送的数据 char buffer[1024]; memset(buffer, 0, sizeof(buffer)); if (recv(client_socket, buffer, sizeof(buffer), 0) == -1) { cerr << "接收数据失败" << endl; close(client_socket); close(server_socket); return -1; } cout << "接收到客户端发送的数据:" << buffer << endl; // 发送数据给客户端 const char *msg = "Hello, I'm server."; if (send(client_socket, msg, strlen(msg), 0) == -1) { cerr << "发送数据失败" << endl; close(client_socket); close(server_socket); return -1; } cout << "发送数据成功" << endl; // 关闭socket close(client_socket); close(server_socket); return 0; } ``` 客户端代码: ```c++ #include <iostream> #include <sys/socket.h> #include <arpa/inet.h> #include <unistd.h> #include <cstring> using namespace std; int main() { // 创建socket int client_socket = socket(AF_INET, SOCK_STREAM, 0); if (client_socket == -1) { cerr << "创建socket失败" << endl; return -1; } cout << "socket创建成功" << endl; // 连接服务器 struct sockaddr_in server_addr{}; server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); server_addr.sin_port = htons(12345); if (connect(client_socket, (struct sockaddr *) &server_addr, sizeof(server_addr)) == -1) { cerr << "连接服务器失败" << endl; close(client_socket); return -1; } cout << "连接服务器成功" << endl; // 发送数据给服务器 const char *msg = "Hello, I'm client."; if (send(client_socket, msg, strlen(msg), 0) == -1) { cerr << "发送数据失败" << endl; close(client_socket); return -1; } cout << "发送数据成功" << endl; // 接收服务器发送的数据 char buffer[1024]; memset(buffer, 0, sizeof(buffer)); if (recv(client_socket, buffer, sizeof(buffer), 0) == -1) { cerr << "接收数据失败" << endl; close(client_socket); return -1; } cout << "接收到服务器发送的数据:" << buffer << endl; // 关闭socket close(client_socket); return 0; } ``` 以上代码仅供参考,具体实现需要根据具体场景进行调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值