【问题标题】:Is there a way to get the negation of if a function exists for an object?有没有办法否定对象是否存在函数?
【发布时间】:2017-10-26 13:16:58
【问题描述】:

要判断某个函数是否存在函数,可以使用如下:

template <typename...Ts>
using void_t = void;

void fn(int);

struct X {};

template <typename T, typename = void_t<decltype(fn(std::declval<T>()))>>
void fn2(T) { }

void test() {
  fn2(int(1)); // works
  //fn2(X()); // doesn't work
}

现在,有没有一种方法可以检测 fn(T) 类型是否不存在 T

例子:

void test2() {
  //fn2(int(1)); // doesn't work
  fn2(X()); // works
}

这样做的原因是定义一个排除操作,以便我可以为两者定义fn2(),以避免产生歧义错误。

【问题讨论】:

  • 我建议将检查包装在类型特征中。然后否定与任何其他特征相同。
  • @Quentin,已修复
  • Aaaaand Clang bug :|没有void_t&lt;&gt; 也能正常工作。
  • @Quentin,是的 耸耸肩。我目前并不担心 Clang 的错误。 ;)
  • @kabanus,已编辑答案以回答您的问题。

标签: c++ c++11 templates sfinae typetraits


【解决方案1】:

通常的做法是创建一个类型特征,正如@chris 所说:

template <typename T, typename = void>
struct fn_callable_with : std::false_type {};
template <typename T>
struct fn_callable_with<T, void_t<decltype(fn(std::declval<T>()))>> : std::true_type {};

// For bonus C++14 points:
// template <typename T>
// /*C++17: inline*/ constexpr bool fn_callable_with_v = fn_callable_with<T>::value;

template <typename T, typename = typename std::enable_if<!fn_callable_with<T>::value>::type>
// C++14: template <typename T, typename = std::enable_if_t<!fn_callable_with_v<T>>>
void fn2(T) { }

【讨论】:

  • 我刚被咬伤:这不是在所有情况下都有效。 fndecltype 探针中的decltype 是一个依赖名称,因此它的查找被推迟到模板的实例化,正如我们需要的那样。但是,查找必须找到fn 通过 ADL。这意味着基本类型的fn 和来自其他命名空间的fn 如果在模板特化之前尚未声明,则不会检测到它们。 See live example.
  • @Quentin - OP 自己的代码很方便地在模板定义之前定义了void fn(int)。这样就可以找到了。您所描述的是模板的常见警告,在阳光下没有什么新鲜事。
  • @StoryTeller 只是期待最坏的情况;)
  • @Quentin - 应该 :) 顺便说一句,在您发布的链接中,我认为您发现了编译器错误。这两个fn应该找不到,但似乎找到了。
  • @StoryTeller Holy ,他们没有要点了吗?这是我一生中见过的最毛茸茸的枚举句子._.
【解决方案2】:

您需要另一个 fn2 重载,否则 SFINAE 不会做任何有用的事情。

void fn2(...) { } // called if the overload below is SFINAEd away

template <typename T, typename = void_t<decltype(fn(std::declval<T>()))>>
void fn2(T) { }

live example on wandbox


这个想法是 SFINAE 可以从重载集合中删除候选人。如果fn2 是唯一的候选者,那么您将遇到一个硬错误。

【讨论】:

  • 是的,我确实需要重载它,但是当它与已经定义的不匹配时,我需要重载它。换句话说,我需要定义一个排除操作,这样我就不会出现歧义错误,同时不会导致硬错误并保持类型。
  • @Adrian,你的意思是你想得到一个编译时错误,如果你尝试重载方法已经实现了? stackoverflow.com/questions/4465686/…
  • 无编译时错误。如果一个函数可以在一个对象上操作,我想要一种方法来选择一个函数,如果不能,我想要另一个函数。
猜你喜欢
  • 2010-11-20
  • 2015-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-02
  • 2016-07-29
  • 2012-05-26
  • 2016-10-01
相关资源
最近更新 更多