Неоднозначное разрешение с оператором преобразования шаблонов
Я должен был сделать подобный код:
#include <type_traits>
template<typename S>
struct probe {
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T& () const;
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T&&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T&& ();
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T const&, U>::value, int> = 0>
operator T const& () const;
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T const&&, U>::value, int> = 0>
operator T const&& () const;
};
struct some_type {};
struct other_type {};
auto test_call(some_type const&, other_type) -> std::false_type;
auto test_call(some_type&, other_type) -> std::true_type;
int main() {
static_assert(decltype(test_call(probe<some_type&>{}, other_type{}))::value, "");
}
Он работает под GCC и Clang, но не компилируется в visual studio с неоднозначной ошибкой разрешения. Какой компилятор не так и почему?
GCC и Clang, Визуальная студия
Вот вывод msvc:
source_file.cpp(31): error C2668: 'test_call': ambiguous call to overloaded function source_file.cpp(28): note: could be 'std::true_type test_call(some_type &,other_type)' source_file.cpp(27): note: or 'std::false_type test_call(const some_type &,other_type)' source_file.cpp(31): note: while trying to match the argument list '(probe<some_type &>, other_type)' source_file.cpp(31): error C2651: 'unknown-type': left of '::' must be a class, struct or union source_file.cpp(31): error C2062: type 'unknown-type' unexpected
1 ответ
Код может быть сокращен до следующего:
#include <type_traits>
struct some_type {};
struct probe {
template<typename T, std::enable_if_t<!std::is_const<T>::value, int> = 0>
operator T& () const;
};
auto test_call(some_type const&) -> std::false_type;
auto test_call(some_type&) -> std::true_type;
int main() {
static_assert(decltype(test_call(probe{}))::value, "");
}
Согласно [temp.deduct.conv] / 5 & 6:
В общем, процесс вывода пытается найти значения аргументов шаблона, которые сделают вывод A идентичным A. Однако, есть четыре случая, которые допускают разницу:
Если исходный A является ссылочным типом, A может быть более квалифицированным по cv, чем выведенный A (т. Е. Тип, на который ссылается ссылка)
...
Эти альтернативы рассматриваются только в том случае, если в противном случае вычет типа не удался. Если они дают более одного возможного вывода A, вывод типа не выполняется.
T
выводится some_type
для обоих вызовов функций. Тогда согласно [over.ics.rank] / 3.3:
Определяемая пользователем последовательность преобразования U1 является лучшей последовательностью преобразования, чем другая определенная пользователем последовательность преобразования U2, если они содержат одну и ту же определяемую пользователем функцию или конструктор преобразования или они инициализируют один и тот же класс в совокупной инициализации и в любом случае вторая стандартная последовательность преобразования U1 лучше, чем вторая стандартная последовательность преобразования U2.
probe -> some_type& -> some_type&
лучше, чем probe -> some_type& -> const some_type&
Так что двусмысленности нет, GCC и Clang правы.
Кстати, если мы удалим std::enable_if_t<...>
В приведенном выше коде MSVC и GCC не работают, пока Clang компилируется. Для дальнейшего анализа я остановлюсь на первом test_all
:
#include <type_traits>
struct some_type {};
struct probe {
template<typename T>
operator T& () const
{
static_assert(std::is_const_v<T>);
static T t;
return t;
}
};
auto test_call(some_type const&) -> std::false_type;
int main() {
test_call(probe{});
}
Тогда мы находим static_assert
стреляет только под Clang. То есть Clang выводит T
быть some_type
вместо const some_type
, Я думаю, что это ошибка Clang.