为什么此代码在 C-stdio 函数重载上表现不同? (vfprintf 与 putchar)

Why does this code behave differently on C-stdio function overloads? (vfprintf vs. putchar)

我正在尝试定义各种与 C stdio 同名的函数,以防止不必要的使用。我遇到了一种奇怪的情况,该技术适用于某些功能,但不适用于其他功能。我无法解释为什么 A::fn 调用 vfprintf 的 stdio 版本而不是 A 命名空间中的函数定义。

#include <stdio.h>
#include <stdarg.h>

namespace A {

    template <typename... Ts>
    void putchar(int ch) {
        static_assert(sizeof...(Ts) == -1);
    }
    
    template <typename... Ts>
    void vfprintf(FILE* file, const char* fmt, va_list vlist) {
        static_assert(sizeof...(Ts) == -1);
    }

    void fn(const char* fmt, ...)
    {
        putchar('A'); // fails to compile (as expected)
        va_list vlist;
        va_start(vlist, fmt);
        vfprintf(stdout, "Hello!\n", vlist); // does not fail (not expected)
        va_end(vlist);
    }
}

int main()
{
    A::fn("hello");
    return 0;
}

P.S。很高兴听到评论表明有更好的方法来限制 C 风格 I/O(也许是 clang-tidy)。

替换标准库例程是 UB(引文如下)。请参阅示例 here and here 了解这可能导致的问题类型。

编辑: 好的,这是承诺的 citation

The C++ standard library reserves the following kinds of names:
...
names with external linkage
...
If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by [library], its behavior is undefined.

但是,正如评论中所讨论的那样,我不确定您是否在允许的上下文中执行此操作(尽管经过深思熟虑,我认为您不是),所以我要改变策略。

事实上,有一种非常简单的方法可以满足您的需求。您可以使用 #pragma GCC poison 执行此操作。因此,以您的示例为例,您只需要:

#pragma GCC poison putchar vfprintf

大功告成。 clang 也支持这个编译指示。

Live demo.

帽子,兔子,我们都能搞定:)(天气好的时候)