szczurcio szczurcio - 3 months ago 15
C++ Question

Template substitution fails when I change the type parameter name - compiler bug?

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.

Answer

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.

Comments