void_t在Visual Studio 2015上失败

18

我不理解为什么以下测试在使用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;
}

这基本上是沃尔特·E·布朗在他的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 bug,还是我做错了什么?

你收到了什么错误信息? - Alan Stokes
7
在Visual Studio中是否已经支持Expression SFINAE?在这里未列出它作为可用的功能:“我们计划在2015 RTM之后立即开始在编译器中实现Expression SFINAE,并计划在2015年更新中交付它,以支持生产使用。(但不一定是2015 Update 1。可能需要更长时间。)” - Piotr Skotnicki
7
“Expression SFINAE” 在 VS2015 中不受支持。 - David G
有趣,谢谢指点,我会研究一下。@Alan:我得到的错误是由于static_assert失败引起的:main.cpp(143):error C2338:失败,而预期结果是干净的编译,因为int是可复制的。 - Federico Sauro
1
所以经过一些调查,我相信问题不是由于表达式SFINAE引起的。我在第二个示例中使用了try_assign位,在那种情况下它可以工作。看起来只是由于void_t引起的一个错误。 - Federico Sauro
2个回答

4

在2014年的CppCon大会上,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问题中,标准不清楚别名模板特化中未使用的参数是否会导致替换失败或者只是被忽略。


1
这是一个有趣的观察,我以前没有尝试过,谢谢。不幸的是,它只适用于“int”和其他可复制分配类型,但对于不可复制分配的类型无法编译。根据VS2015 spit出的错误信息看起来,SFINAE与那个惯用语不匹配(但为什么?)。例如,如果您尝试使用class U {U operator =(U const&)= delete;};进行尝试,并且它可以与我发布的第一个惯用语一起工作,您将收到错误消息。关于CWG1558,您是正确的,这与其无关,因为VS已实现解决方法(只需查看std :: void_t的定义方式即可)。 - Federico Sauro
实际上,您提出的is_valid解决方案并没有遭受普通void_t方法的缺陷。它无法编译class U {U operator=(U const&) = delete;};因为该运算符是私有的 - 这仍然不理想(在GCC中运行良好),但这是一个不同的问题。 - Federico Sauro

3

这看起来是VC++中的SFINAE问题。在类模板的部分特化的模板参数中使用依赖的decltype目前还不支持。在VS 2015 Update 1中应该能够正常工作。


1
不幸的是,它仍然无法在VS 2015更新1 RC中运行。 - Federico Sauro
1
你说得对,但我相信这个问题已经在更新1 RTW中得到了修复。我刚在VC++ Web编译器上验证了修复:http://webcompiler.cloudapp.net/ - apardoe
1
我刚刚在VS2015升级2版本上进行了测试,可以确认此问题也已被修复。 - Federico Sauro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接