专门针对所有类型指针的函数模板

问题描述 投票:0回答:1

我想实现一个流式日志库。

我创建了一个

Log_t
类来缓冲日志条目,并对其进行实际输出 被毁了,就像这样:

class Log_t: public std::ostringstream {
    static int log_id;
public:
    Log_t() { ++log_id; };
    ~Log_t() override {
        // in order to simplify discussion, here output it to cout
        std::cout << "log" << log_id << ':' << str() << std::endl;
    };
};

int Log_t::log_id {};

template <typename T>
Log_t& operator<<( Log_t& log_, const T& body_ ) {
    static_cast<std::ostream&>( log_ ) << body_;
    return log_;
};

每当我有自定义的类/结构时,我都可以用相同的方式记录它,只要 我为它实现了一个输出函数。像这样:

struct Response_t {
    int _id;
};

// Using std::ostream instead of Log_t, as it may be outputed to other targets
std::ostream& operator<<( std::ostream& os_, const Response_t& rsp_ ) {
    return os_ << "resp_id:" << rsp_._id;
};

然后我可以将其记录为:

Response_t response {123};
Log_t() << "local obj=" << response;

但实际上Response_t主要是由lib返回,并且以指针形式返回 一个真正的ojb/ref,所以我可能应该输出所指向的内容 而不是地址,并且在记录之前我必须检查它是否为 nullptr。 此外,我不想检查每个自定义输出中是否为 nullptr 函数,相反,我只想在日志记录函数模板中检查一次。 我把函数模板改成了这两个:

template <typename T>
Log_t& operator<<( Log_t& log_, const T* body_ ) {
    if( body_ == nullptr )
        static_cast<std::ostream&>( log_ ) << "{nullptr}";
    else
        static_cast<std::ostream&>( log_ ) << *body_;
    return log_;
};

template <typename T>
Log_t& operator<<( Log_t& log_, const T& body_ ) {
    static_assert( ! std::is_null_pointer_v<T> );
    static_assert( ! std::is_pointer_v<T> );

    static_cast<std::ostream&>( log_ ) << body_;
    return log_;
};

完整代码:

#include <iomanip>
#include <iostream>
#include <sstream>
#include <type_traits>

class Log_t: public std::ostringstream {
    static int log_id;
public:
    Log_t() { ++log_id; };
    ~Log_t() override { std::cout << "log" << log_id << ':' << str() << std::endl; };
};

int Log_t::log_id {};

/*
    Let's say that parameter body_ is a pointer received from another lib,
    so it may be a nullptr, and I don't wana check if it is a nullptr
    everywhere in my projects, instead I want to check it only once in the
    following function.
*/
template <typename T>
Log_t& operator<<( Log_t& log_, const T* body_ ) {
    if( body_ == nullptr )
        static_cast<std::ostream&>( log_ ) << "{nullptr}";
    else
        static_cast<std::ostream&>( log_ ) << *body_;
    return log_;
};

template <typename T>
Log_t& operator<<( Log_t& log_, const T& body_ ) {
    static_assert( ! std::is_null_pointer_v<T> );
    static_assert( ! std::is_pointer_v<T> );

    static_cast<std::ostream&>( log_ ) << body_;
    return log_;
};

struct Response_t {
    int _id;
};
std::ostream& operator<<( std::ostream& os_, const Response_t& rsp_ ) {
    return os_ << "resp_id:" << rsp_._id;
};

int main() {
    Response_t response {123};
    Log_t() << "local obj=" << response;

    // Suppose rsp_ptr is received from elsewhere, may be nullptr!
    Response_t* rsp_ptr = &response;
    Log_t() << "ptr obj=" << rsp_ptr;
    rsp_ptr = nullptr;
    Log_t() << "ptr obj=" << response;

    exit( EXIT_SUCCESS );
};

但是我可以不能通过编译,并得到这个:

ostream-ptr.cpp:36:31:错误:静态断言失败 36 |
static_assert( ! std::is_pointer_v );

看起来指针的专门版本已经被gcc-12.3忽略了?

也许我可以用c++20的

concept
来解决它? 如何编码?

c++ sfinae c++-concepts overload-resolution partial-specialization
1个回答
0
投票

const T*
超载并没有被忽视。
const T&
重载更加专业化。

考虑这个例子:

template <typename T>
void f(const T*) {}

template <typename T>
void f(const T&) {}

int main() {
    int i;
    f(i); // calls the ref overload as expected, with T = int
    int* p = &i;
    f(p); // still calls the ref overload, but with T = int*
    const int* pc = &i;
    f(pc); // correctly calls the pointer overload, with T = int
}

演示

如果添加第三个重载

void f(T*)
而没有
const
,则第二个调用 (
f(p)
) 将使用该重载。

我假设您希望每当传递指针时都调用

const T*
重载,无论是否
const
。在这种情况下,在 C++20 中,您可以添加
requires
子句来禁用
const T&
重载:

template <typename T>
requires(!std::is_pointer_v<T>)
Log_t& operator<<( Log_t& log_, const T& body_ ) { ... }

演示

C++20之前,您可以类似地使用SFINAE。

© www.soinside.com 2019 - 2024. All rights reserved.