我想我只是做了一个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
}

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

我的问题是:这里到底发生了什么?这是标准定义的行为,还是我尝试过的所有编译器都以相同方式处理的未定义条件,还是我尝试过的所有编译器中同时出现的错误?

最佳答案

我怀疑[temp.inst]可以简单地覆盖所有内容:



无论您以哪种方式定义has_f,都涉及无限递归。 has_f<Bar>涉及f(Bar )的实例化,其中has_f<Bar>的实例化涉及...的实例化...

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

关于c++ - C++函数,仅在不存在时才启用,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36333003/

10-11 00:46