void_t 在 Visual Studio 2015 年失败

void_t fails on Visual Studio 2015

我不明白为什么以下测试总是因 Visual Studio 2015(static_assert 触发器)而失败:

#include <type_traits>
using namespace std;

template<class T> using try_assign = decltype(declval<T&>() = declval<T const&>());
template<class, class = void> struct my_is_copy_assignable : false_type {};
template<class T> struct my_is_copy_assignable<T, void_t<try_assign<T>>> : true_type {};

int main()
{
    static_assert(my_is_copy_assignable<int>::value, "fail");
    return 0;
}

这基本上是 Walter E Brown 在他的 cppcon 2014 演讲 "Modern Template Metaprogramming - A compendium".

中对 void_t 的示例用法的转录

请务必注意,此替代版本有效,因此我认为问题不在于 MSVC 对表达式 SFINAE 的不完整支持。

template<class T>
using try_assign = decltype(declval<T&>() = declval<T const&>());

template<class T>
struct my_is_copy_assignable
{
  template<class Q, class = try_assign<Q>>
  static true_type tester(Q&&);
  static false_type tester(...);
  using type = decltype(tester(declval<T>()));
};

我知道 std::is_copy_assignable,但我只是想更好地了解 C++ 不同修订版中可用的各种元编程技术。我在网上阅读了几个关于 void_t 的话题,但我仍然不明白为什么这个例子失败了。

有趣的是,使用 GCC 4.8.2 它工作正常(使用 CWG 1558 解决方法,这与 Microsoft 的版本相同)。

这是一个已知的 Visual Studio 错误,还是我做错了什么?

Walter E. Brown 在 CppCon 2014 上还提到了以下因式分解,它允许用任意条件替换 try_assign

#include <type_traits>
#include <utility>

template<class T>
using try_assign = decltype(std::declval<T&>() = std::declval <T const &>());

template<class T, template<class> class Op, class = void>
struct is_valid : std::false_type { };

template<class T, template<class> class Op>
struct is_valid<T, Op, std::void_t<Op<T>>> : std::true_type { };

template<class T>
using is_copy_assignable = is_valid<T, try_assign>;

int main()
{
    static_assert(is_copy_assignable<int>::value, "fail");
    return 0;
}

此分解在 VS 2015 中编译正常。现在删除 is_copy_assignable 并代入 is_valid。您最终得到了您提供的代码,但无法编译 (VS 2015)。

这表明 VS 2015 中存在一个错误,它与 CWG 1558 无关。在 CWG 问题中,标准不清楚别名模板特化中未使用的参数是否会导致替换失败或被简单地忽略。

这看起来确实像是 VC++ 中的 SFINAE 问题。尚不支持在 class 模板的偏特化模板参数中使用依赖 decltype。它应该在 VS 2015 Update 1 中工作。