【问题标题】:Create and Call an Empty Function创建和调用空函数
【发布时间】:2019-02-26 15:43:40
【问题描述】:

给定一个函数签名(例如来自 template 参数),我想使用该签名创建一个(非内联)函数,然后调用它。

以下内容应阐明此意图。正如所写,它是无效的,因为标记的行实际上生成了一个空函数指针,而不是一个空函数:

template <typename Function, typename... Args>
void foo(Args... args) {
    Function fn_trivial = {}; //incorrect; should be empty function, not null function
    fn_trivial(args...);
}

如何在这里创建函数对象?


(旁注:这似乎是一件奇怪的事情。原因是它是一个通用分析器,它调用(标记为不可内联)测试函数n 次来计算平均延迟. 测试代码成本的(更好)相似性可以通过减去具有相同参数的函数调用的延迟来获得,平均而言,它什么都不做。)

【问题讨论】:

  • Function 很可能是一个 f 指针,在这种情况下,初始化会将其设置为 nullptr。 “空”函数应该做什么?
  • 关于你的旁注:如果函数什么都不做,那么它的调用是一个很好的优化对象
  • @SombreroChicken OP 很可能意味着 NOP。
  • 将其设为本地 lambda 并调用它。
  • 普通函数是否应该返回void?无论如何,您的用例可能超出了 C++ 标准的编写目的(因为您正在积极地与编译器抗争以不优化调用)。也许您应该弄清楚所需的程序集是什么样子并从那里开始工作。

标签: c++ function templates


【解决方案1】:

您不能在 C++ 中创建本地函数。您可以创建一个本地对象类型并定义/调用它的operator(),或者使用 lambda — 但此时您不妨将它作为一个真正的函数放在外面:

template <typename TypeRet, typename... Args>
TypeRet empty_function(Args... args) {
    return TypeRet();
}

template <typename Function, typename... Args>
void foo(Args... args) {
    empty_function<decltype( std::declval<Function>()(args...) )>(args...);
}

(考虑也添加std::forward,根据需要。)

请注意,任何有价值的编译器都会删除该调用,即使它被标记为不可内联。这是因为即使该函数没有内联,它什么也不做,因此可以完全省略。您可以通过多种方式解决此问题,但使用 volatile 变量可以移植到 Clang、GCC、ICC 和 MSVC:

#ifdef _MSC_VER
    #define NOINLINE __declspec(noinline)
#else
    #define NOINLINE __attribute__((noinline))
#endif
template <typename TypeRet, typename... Args>
NOINLINE TypeRet empty_function(Args... args) {
    TypeRet volatile a = TypeRet();
    return a;
}

See it live.

【讨论】:

  • @Quentin 已修复。虽然我刚刚意识到 lambda 会更容易,所以我也会添加它。
  • 我显着修改了您的答案以解决一些问题,添加一些解释性文本,并演示非内联问题的解决方法。然后我接受了。
猜你喜欢
  • 2020-07-20
  • 2012-07-02
  • 1970-01-01
  • 2021-08-17
  • 2013-12-26
  • 1970-01-01
  • 2021-12-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多