Неоднозначное разрешение с оператором преобразования шаблонов - PullRequest
0 голосов
/ 24 июня 2018

Мне пришлось сделать похожий код:

#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 , Visual studio

Вот вывод 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 Ответ

0 голосов
/ 29 июня 2018

Код может быть уменьшен до следующие :

#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.

...