C++ SFINAE 没有失败

Hrv*_*rić 6 c++ templates sfinae

代码:

#include <iostream>


using std::nullptr_t;

template<typename... T>
using nullptr_vt = nullptr_t;

struct not_addable{};


template<
  typename T, 
  nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> TSfinae = nullptr>
bool test_addable(int)
{ return true; }

template<typename>
bool test_addable(...)
{ return false; }


int main()
{
  std::cout << std::boolalpha;

  std::cout << test_addable<int>(0) << std::endl;
  std::cout << test_addable<not_addable>(0) << std::endl;

  // Gives error ("invalid operands to binary expression"):
  // nullptr_vt<decltype(std::declval<not_addable>() + std::declval<not_addable>())> a{};
}
Run Code Online (Sandbox Code Playgroud)

我以为这会打印:

true
false
Run Code Online (Sandbox Code Playgroud)

,但事实并非如此。它打印:

true 
true
Run Code Online (Sandbox Code Playgroud)

. 至少在https://repl.it/@Hrle/sfinaetemplatesuccess 上

我认为nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>从第一个重载将是一个错误not_addable,它会从重载集中丢弃它,从而选择第二个重载。 如果存在默认值

,编译器是否有能力丢弃类型TSfinae

Use*_*ess 3

我认为nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>从第一个重载开始,not_addable 就会出现错误,并且它会将其从重载集中丢弃,从而选择第二个重载。

这个想法实际上很好,问题在于 GCC 和nullptr_vt

这行:

nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> TSfinae = nullptr
Run Code Online (Sandbox Code Playgroud)

在 GCC 10.2 上您不希望它工作的地方可以工作,但在 Clang 11.0.1 上却是正确的。将其更改为

nullptr_vt<decltype(std::declval<T>() + std::declval<T>())> *TSfinae = nullptr
Run Code Online (Sandbox Code Playgroud)

两者都是正确的,更简单的也是正确的

typename TSfinae = nullptr_vt<decltype(std::declval<T>() + std::declval<T>())>
typename _ = decltype(std::declval<T>() + std::declval<T>())
Run Code Online (Sandbox Code Playgroud)

最后是 make_void 技巧

template<typename... T> struct make_nullptr_vt { using type = nullptr_t; };

template<typename T>
using nullptr_vt = typename make_nullptr_vt<T>::type;
Run Code Online (Sandbox Code Playgroud)

还修复了 GCC 上的原始版本。