gpt4 book ai didi

c++ - 编译时专门针对函数指针引用以避免 -Waddress

转载 作者:太空狗 更新时间:2023-10-29 20:19:36 24 4
gpt4 key购买 nike

我遇到了 GCC 的以下问题(使用 v6.4 测试它在当前主干上的行为相同)可以简化为以下简约示例:

我们进一步研究可调用对象,例如实现 operator()operator bool 的类以及函数指针,例如 void(*)() 和函数引用 void(&)()

以下问题可能与提前阅读相关:

我正在尝试实现一个条件调用,它会在调用可调用对象之前检查其转换为 bool 是否为 true,然后再调用它:

/// g++-6.4 -O3 -std=c++17 -Wall

#include <functional>
#include <type_traits>

template <typename O>
void safe_invoke(O&& fn) {
if (fn) {
std::invoke(std::forward<O>(fn));
}
}

void somefn() { }

int main() {
safe_invoke(somefn);
return 0;
}

使用 GCC 和 -Wall 时会产生警告

In instantiation of 'void safe_invoke(O&&) [with O = void (&)()]':
warning: the compiler can assume that the address of 'fn' will always evaluate to 'true' [-Waddress]
if (fn) {
^~

如警告所示,GCC 使用 void(&)() 作为可调用类型 O 的正确引用类型。我处理此警告的方法是,我想完全摆脱 bool(callable) 通过专门化具有特定特征的函数引用来检查永远不会为 null 的函数引用:

/// g++-6.4 -O3 -std=c++17 -Wall -Werror
/// https://gcc.godbolt.org/z/2TCaHq

#include <functional>
#include <type_traits>

template <typename T>
struct invoke_trait {
template <typename O>
static void invoke(O&& fn) {
if (fn) {
std::invoke(std::forward<T>(fn));
}
}
};

template <typename Ret, typename... Args>
struct invoke_trait<Ret (*)(Args...)> {
template <typename O>
static void invoke(O&& fn) {
if (fn) {
std::invoke(std::forward<O>(fn));
}
}
};

template <typename Ret, typename... Args>
struct invoke_trait<Ret (&)(Args...)> {
template <typename O>
static void invoke(O&& fn) {
std::invoke(std::forward<O>(fn));
}
};

template <typename O>
void safe_invoke(O&& fn) {
using trait_t = invoke_trait<std::decay_t<O>>;
trait_t::invoke(std::forward<O>(fn));
}

void test() {
}

int main() {
// No compile error as expected:
{
using fn_t = void (*)();
fn_t fn = nullptr;

safe_invoke(fn);
}

// the compiler can assume that the address of 'fn' will always evaluate
// to 'true' [-Werror=address]
{
safe_invoke(test);
}

// the compiler can assume that the address of 'fn' will always evaluate
// to 'true' [-Werror=address]
{
using fn_ref_t = void (&)();
fn_ref_t fn_ref = test;

safe_invoke(fn_ref);
}

return 0;
}

https://gcc.godbolt.org/z/3QAKpf

遗憾的是 GCC 在这里失败了并且总是使用 Ret (*)(Args...) 的特化。我的代码是否存在问题,阻止了对 Ret (&)(Args...) 的正确特化,或者这种特化可以以不同的方式完成吗?此外,是否有其他方法可以在不显式抑制 GCC 警告的情况下阻止它(尽管这可能不是最佳解决方案)?

最佳答案

std::decay_t<O>

这会将函数引用转换为函数指针。

用删除引用和删除 CV 的组合替换衰减。然后专注于 F(Args...)F(*)(Args...) 而不是 F(&)(Args...) F(*)(Args...)

template <typename Ret, typename... Args>
struct invoke_trait<Ret (*)(Args...)> {
template <typename O>
static void invoke(O&& fn) {
if (fn) {
std::invoke(std::forward<O>(fn));
}
}
};

template <typename Ret, typename... Args>
struct invoke_trait<Ret(Args...)> {
template <typename O>
static void invoke(O&& fn) {
std::invoke(std::forward<O>(fn));
}
};

template <typename O>
void safe_invoke(O&& fn) {
using trait_t = invoke_trait<std::remove_cv_t<std::remove_reference_t<O>>>;
trait_t::invoke(std::forward<O>(fn));
}

这应该有效,最多可以有拼写错误。

关于c++ - 编译时专门针对函数指针引用以避免 -Waddress,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57981757/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com