Большая часть следующего кода была взята из ответа Петра Скотницкого. Я экспериментировал с ним и обнаружил ошибку в MSVC 14.0 Update 3.
Рассмотрим следующий код:
#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();
}
Это напечатает true
, как и ожидалось, потому что лямбда-объект, сгенерированный компилятором, действительно реализует operator()
.
Давайте теперь изменим имя параметра типа в is_callable_object
с L
на T
(из того, что я вижу, все, что отличается от имени типа, используемого в is_convertible_to_function
, вызывает эту проблему).
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 {};
Внезапно это печатает false
. is_convertible_to_funtion
не имеет значения, так как is_callable_object
никоим образом на него не полагается; действительно, если я уберу is_convertible_to_function
, эта проблема исчезнет — я могу использовать любое имя типа, какое захочу.
Как я уже сказал, я подозреваю, что это ошибка, поэтому я задаю этот вопрос, чтобы убедиться, что это не какое-то странное поведение в стандарте C++; обходной путь для этого довольно прост.