Подстановка шаблона завершается ошибкой, когда я изменяю имя параметра типа - ошибка компилятора?

Большая часть следующего кода была взята из ответа Петра Скотницкого. Я экспериментировал с ним и обнаружил ошибку в 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++; обходной путь для этого довольно прост.


person user4520    schedule 16.08.2016    source источник


Ответы (1)


Выражение sfinae не работает в msvc 2015. Любые случаи, когда оно работает, случайны, не доверяйте ему. decltype не может использоваться для надежного вызова sfinae в msvc 2015.

Остановитесь, уйдите и найдите другое решение. Возможно, попробуйте встроенные функции компилятора.

Не думайте, что случайное срабатывание при совпадении имен параметров означает, что все остальное будет работать или будет работать в немного другой программе.

Вашему обходному пути нельзя доверять.

person Yakk - Adam Nevraumont    schedule 16.08.2016
comment
Мне интересно, не было ли ваше заявление слишком экстремальным, но даже сами Microsoft признают это: blogs.msdn.microsoft.com/vcblog/2015/12/02/. Ок, буду экспериментировать, спасибо. - person user4520; 17.08.2016