【问题标题】:void_t fails on Visual Studio 2015void_t 在 Visual Studio 2015 上失败
【发布时间】:2015-11-01 16:17:01
【问题描述】:

我不明白为什么以下测试在 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 演讲“现代模板元编程 - 纲要”中对 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 错误,还是我做错了什么?

【问题讨论】:

  • 你得到什么错误信息?
  • 在 VS 中是否已经支持表达式 SFINAE?它未列为可用here“我们计划在 2015 RTM 之后立即开始在编译器中实现 Expression SFINAE,我们计划在 2015 的更新中提供它,支持生产使用。(但不一定是 2015 Update 1。可能需要更长的时间。)"
  • 有趣,感谢您的指点,我会研究一下。 @Alan:我得到的错误是由于 static_assert 失败:main.cpp(143): error C2338: fail,而预期的结果是干净的编译,因为 int 是可复制的。
  • 所以经过一番调查,我认为问题不在于表达式 SFINAE。我在第二个示例中使用了try_assign 位,在这种情况下有效。由于 void_t,看起来真的只是一个错误

标签: c++ visual-studio-2015 c++14 sfinae


【解决方案1】:

CppCon 2014 上的 Walter E. Brown 还提到了以下分解,它允许用任意条件替换 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 问题中,标准不清楚别名模板特化中未使用的参数是否会导致替换失败或被忽略。

【讨论】:

  • 这是一个有趣的观察,我之前没有尝试过,谢谢。不幸的是,它仅适用于int 和其他可复制分配的类型,但无法使用不可复制的可分配类型进行编译。从 VS2015 吐出的错误来看,SFINAE 似乎没有使用该成语(但为什么??)。例如,如果您尝试使用class U {U operator=(U const&amp;) = delete;};,它适用于我发布的第一个成语,您将收到错误消息。关于 CWG1558,你是对的,它不相关,因为 VS 已经实现了解决方法(只需看看 std::void_t 是如何定义的)。
  • 实际上,您提出的使用 is_valid 的解决方案不会受到普通 void_t 方法的缺陷的影响。使用类 U {U operator=(U const&) = delete;}; 编译失败因为操作员是私有的 - 仍然不受欢迎(并且在 GCC 中工作正常)但这是一个不同的问题。
【解决方案2】:

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

【讨论】:

  • 不幸的是,VS 2015 update 1 RC 仍然失败
  • 你是对的,但我相信它已在更新 1 RTW 中修复。我刚刚在 VC++ Web 编译器上验证了修复:webcompiler.cloudapp.net
  • 我刚刚使用 VS2015 update 2 对其进行了测试,我可以确认它也已修复。
猜你喜欢
  • 2015-10-28
  • 1970-01-01
  • 2017-03-07
  • 1970-01-01
  • 2016-08-03
  • 2017-02-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多