Код может быть уменьшен до следующие :
#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.