18

I don't understand why the following test always fails with Visual Studio 2015 (the static_assert triggers):

#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;
}

It's basically the transcription of Walter E Brown's example usage of void_t from his cppcon 2014 presentation "Modern Template Metaprogramming - A compendium".

It's important to note that this alternate version works so I don't think that the problem lies in MSVC's incomplete support to expression 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>()));
};

I know about std::is_copy_assignable but I'm just interested in better understanding the various metaprogramming techniques available in the different revisions of C++. I read several threads about void_t on the web but I still don't understand why this example fails.

The interesting thing is that with GCC 4.8.2 it works fine (using the CWG 1558 workaround, which is the same that the Microsoft's version does).

Is it a known Visual Studio bug, or am I doing something wrong?

Lukáš Bednařík
  • 2,578
  • 2
  • 15
  • 30
  • What error message do you get? – Alan Stokes Aug 09 '15 at 19:14
  • 7
    Is Expression SFINAE already supported in VS ? It's not listed as available [here](http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx): *"We're planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we're planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)"* – Piotr Skotnicki Aug 09 '15 at 19:16
  • 7
    [Expression SFINAE isn't supported in VS2015](http://blogs.msdn.com/b/vcblog/archive/2014/11/17/c-11-14-17-features-in-vs-2015-preview.aspx). – David G Aug 09 '15 at 19:23
  • Interesting, thanks for the pointers, I'll look into that. @Alan: the error I get is due to the static_assert failing: main.cpp(143): error C2338: fail, while the expected result is a clean compile because int is copy assignable. – Federico Sauro Aug 09 '15 at 19:50
  • 1
    So after some investigation I believe that the problem is not due to expression SFINAE. I'm using the `try_assign` bit in the second example and in that case works. Really looks like just a bug due to void_t – Federico Sauro Aug 13 '15 at 20:32

2 Answers2

4

Walter E. Brown at CppCon 2014 also mentioned the following factorization which allows to replace try_assign with arbitrary condition.

#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;
}

This factorization compiles OK with VS 2015. Now remove is_copy_assignable and substitute into is_valid. You end up with the code you presented and which doesn't compile (VS 2015).

This suggests there's a bug in VS 2015 and it's not related to CWG 1558. In CWG issue the standard was unclear whether unused arguments in alias template specializations could result in substitution failure or are simply ignored.

Lukáš Bednařík
  • 2,578
  • 2
  • 15
  • 30
  • 1
    That's an interesting observation, I did not try that before, thanks. Unfortunately it only works for `int` and other copy assignable types, but fails to compile with non copy assignable ones. From the error that VS2015 spits it looks like SFINAE is not kicking in with that idiom (but why??). If you for example try with `class U {U operator=(U const&) = delete;};`, which works with the first idiom I posted, you will get the error. About CWG1558 you are right, it's not related because VS already implements the workaround (just look at how std::void_t is defined). – Federico Sauro Oct 06 '15 at 08:41
  • Actually, your proposed solution with is_valid does not suffer from the defect of the plain void_t approach. It's failing to compile with class U {U operator=(U const&) = delete;}; because the operator is private - still undesirable (and works fine in GCC) but it's a different issue. – Federico Sauro Nov 01 '15 at 18:59
3

This does look like a SFINAE issue in VC++. Using a dependent decltype in the template argument of partial specialization of a class template isn't yet supported. It should work in VS 2015 Update 1.

slfan
  • 8,950
  • 115
  • 65
  • 78
apardoe
  • 631
  • 6
  • 10