C ++函数只有在不存在的情况下才会启用

时间:2016-03-31 12:06:31

标签: c++ c++14 sfinae

我想我刚刚做了一个C ++悖论......

#include <type_traits>
#include <utility>

// If has_f trait is defined in this way, compilation breaks because of infinite recursion in template substitution
/*
template< typename T, typename Enable=void >
struct has_f : std::false_type { };

template< typename T >
struct has_f<T, decltype(f(std::declval<T>()))> : std::true_type { };
*/

// Defining has_f like this works on MSVC, gcc and CLang
namespace has_f_impl {
  struct no{ };
  template< typename T >
  no check(...);
  template< typename T >
  decltype(f(std::declval<T>())) check(void const*);

  template< typename T >
  struct has_f : std::integral_constant<bool, !std::is_same<no, decltype(check<T>(nullptr))>::value> { };
}
using has_f_impl::has_f;

struct Foo { };
struct Bar { };

template< typename T, std::enable_if_t<std::is_same<Foo, T>::value, int> = 0 >
void f(T const&);

template< typename T, std::enable_if_t<!has_f<T const&>::value, int> = 1 >
void f(T const&);

int main() {
  f(Foo()); // Calls f<Foo,0>()
  f(Bar()); // Calls f<Bar,1>()
  f(Foo()); // Calls f<Foo,0>()
  f(Bar()); // Calls f<Bar,1>()
}

以上代码令人惊讶地工作,并且以非常聪明的方式,只有在没有其他选项的情况下才使用通用f

此外,这可能是因为ODR,会发生以下情况

// Includes, has_f, Foo, Bar and f as above
template< typename T, std::enable_if_t<has_f<T const&>::value>* = nullptr >
void g(T const&);

int main() {
  f(Foo()); // Calls f<Foo,0>()
  f(Bar()); // Calls f<Bar,1>()
  f(Foo()); // Calls f<Foo,0>()
  f(Bar()); // Calls f<Bar,1>()
  g(Foo());
  //g(Bar()); //No such function
}

据我所知,所有这些似乎都与声明顺序无关。

我的问题是:这里到底发生了什么?这是一个标准定义的行为,一个未定义的条件,我试图以相同的方式处理所有编译器,或者在我尝试的所有编译器中巧妙存在的错误?

1 个答案:

答案 0 :(得分:2)

我怀疑这只是[temp.inst]所涵盖的所有内容:

  

实例化中无限递归的结果是   未定义。

无论您定义has_f的方式如何,都涉及无限递归。 has_f<Bar>涉及f(Bar )的实例化,涉及has_f<Bar>的实例化,涉及实例化......

事实上,一种定义has_f的方法在某些情况下有效但在其他情况下无效,而另一种方式肯定不起作用,这只是未定义行为的结果。未定义的行为未定义。