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