2

Most of the following code has been taken from an answer by Piotr Skotnicki. I was experimenting with it and discovered what I believe to be a bug in MSVC 14.0 Update 3.

Consider the following code:

#include <iostream>

template <typename T>
struct identity { using type = T; };

template <typename...>
using void_t = void;

template <typename F>
struct call_operator;

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...)> : identity<R(A...)> {};

template <typename C, typename R, typename... A>
struct call_operator<R(C::*)(A...) const> : identity<R(A...)> {};

template <typename F>
using call_operator_t = typename call_operator<F>::type;

template <typename, typename = void_t<>>
struct is_convertible_to_function
    : std::false_type {};

template <typename L>
struct is_convertible_to_function<L, void_t<decltype(&L::operator())>>
    : std::is_assignable<call_operator_t<decltype(&L::operator())>*&, L> {};

template <typename, typename = void_t<>>
struct is_callable_object
    : std::false_type {};

template <typename L>
struct is_callable_object<L, void_t<decltype(&L::operator())>>
    : std::true_type {};


int main()
{
    auto x = []() {};
    std::cout << std::boolalpha << is_callable_object<decltype(x)>::value;
    std::getchar();
}

This prints true, as expected, because the lambda object generated by the compiler does implement operator().

Let's now change the type parameter name in is_callable_object from L to T (anything different from the type name used in is_convertible_to_function causes this problem, from what I see).

template <typename, typename = void_t<>>
struct is_callable_object
    : std::false_type {};

template <typename T>
struct is_callable_object<T, void_t<decltype(&T::operator())>>
    : std::true_type {};

Suddenly, this prints false. is_convertible_to_funtion shouldn't matter since is_callable_object doesn't rely on it in any way; indeed, if I remove is_convertible_to_function, this problem disappears - I can use any type name I want.

As I said, I suspect this is a bug, so I'm asking this question to make sure that this isn't some bizarre behavior in the C++ standard; the workaround for this is quite easy.

Community
  • 1
  • 1
user4520
  • 3,401
  • 1
  • 27
  • 50

1 Answers1

2

Expression sfinae is non-functional in msvc 2015. Any cases where it works are accidental, do not trust it. decltype cannot be used to cause sfinae in msvc 2015 reliably.

Stop, walk away, and find another solution. Maybe try compiler intrinsics.

Do not assume the accidental working when parameter names match means anything else will work, or it will work in a slightly different program.

Your workaround cannot be trusted.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I was wondering if your statement wasn't a bit too extreme, but even Microsoft themselves admit it: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/. Oh well, I'll keep experimenting, thank you. – user4520 Aug 17 '16 at 05:17