如何在编译时检查函数是否被调用

18
我正在尝试在编译时确定函数是否被调用。具体来说,如果该函数被调用,我想抛出一个静态断言失败:
template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
  // this is the function I want to call
}

template <typename... T>
int Function(T...)
{
  // This function should never be called, instead I want
  // a compile-time failure if this is called, because it
  // means the above function wasn't successfully resolved.
}

我希望这样做的原因是,如果使用错误的条件调用Function(),将导致成千上万行的编译器错误消息,对于不熟悉代码库的人来说,这些消息并没有什么帮助。
我不想在Function中放置static_assert的原因是因为我们有很多这样的函数,并且我们有手段通过宏生成Catch-all版本,这可以避免代码库不必要的增长,同时产生更有用的错误消息。
这个能做到吗?

@Arman,我的评论意在表明你的标题是虚假的。 - 101010
@hvd 不行,因为宏位于不同的位置,并且我想要调用的“Function”不是由宏生成的。我之所以想这样做,是因为宏(在其他地方)仍然可以生成 catch-all 函数。 - quant
1
@hvd 噢,我问题的实质是如何将静态断言放在那里,以便只有在函数确实从某个地方调用时才会触发。如果我只是在那里放一个 static_assert(false,...),它就会失败,对吧? - quant
1
@Arman 对,那样做不会成功。所以不要这样做 :) 我已经发布了一个有效的答案。 - user743382
@PhilippClaßen 是的,但我会得到50,000行错误消息,对于不太了解代码的人来说几乎是不可能理解的。我正在尝试创建更有用的错误消息... - quant
显示剩余4条评论
2个回答

21

根据您问题中的评论,您不需要在此处使用static_assert

template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
  // this is the function I want to call
}

但其实在这里使用static_assert是没有问题的:

template <typename... T>
struct dependent_false { static constexpr bool value = false; };

template <typename... T>
int Function(T...)
{
  static_assert(dependent_false<T...>::value, "you are passing the wrong arguments!");
}

正如您正确指出的那样,一个简单的static_assert(false, "...");会在模板定义时失败。要得到一个仅在实例化时失败的东西,您需要一个依赖表达式,并且dependent_false助手结构是一种简单的方式来获取某些将是类型依赖性的东西,几乎总是false,但编译器不能假定它真的总是false:编译器无法排除您添加部分特化使得对于某些类型dependent_false<...>::value成为true
回顾这个老问题,可能有一个更简单的答案:将重载标记为已删除。
template <typename T>
auto Function(T value) -> std::enable_if<someCondition, int>
{
    // this is the function I want to call
}

template <typename... T>
int Function(T...) = delete;

这并不完全相同,因为它允许调用者检查例如Function(int, int)的格式是否正确,而不是强制出现错误,但它更易读,并且通常您希望只有在实际使用函数而不是仅引用时才能获得该确切行为,而不是出现错误。


1
为什么不使用 static_assert(false, "...") - David G
3
由于这通常会在模板定义时失败,而不是在模板实例化时失败。 - user743382
你也可以像这样做:static_assert(sizeof(T) != sizeof(T), ...,这可以节省一些代码,但有点恶心。 - Riot
@Riot 可能存在一个标准库类型模板可以用于此,但需要是一个在返回false的表达式中对所有类型都适用的模板,同时仍允许用户为某些类型特化模板使表达式返回true。例如,std::integral_constant<T *, nullptr>::value等简单示例并不适用,因为用户不被允许特化std::integral_constant以使value变为任何其他值而非nullptr。即使你找到了这样的类型,我担心它将会混乱不堪,建议不要使用。 - user743382
@Riot 根据具体需求,可能比我最初回答的要简单得多。编辑以提供另一种选择。 - user743382
显示剩余6条评论

0

仅在所接受的答案上再加补充一点:
我们还可以定义以下内容

template <class... T>
inline constexpr bool dependant_false_v = dependant_false<T...>::value;

然后使用:

template <typename... T>
int Function(T...)
{
  static_assert(dependent_false_v<T...>, "you are passing the wrong arguments!");
}

在我看来,哪个更干净且更短。

使用C++17,我们甚至可以通过constexpr if摆脱SFINAE的enable_if并将所有内容合并到一个函数中:

template <typename T>
auto Function(T value)
{
  if constexpr(someCondition) {
    // Code for your function
  } else {
    static_assert(dependent_false_v<T>, "you are passing the wrong arguments!");
  }
}

我发布这篇文章的原因是我在阅读有关constexpr if的博客时,通过链接跳转到了这里,以避免出现不良的static_assert(false),请参见此处:https://blog.tartanllama.xyz/if-constexpr/。 - clocktown

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接