Как компилятор определяет между функцией, использующей SFINAE, и стандартной функцией, являются ли они жизнеспособными? - PullRequest
4 голосов
/ 21 февраля 2020

Рассмотрим следующий код:

#include <iostream>
#include <type_traits>

template <typename T>
class A
{
public:
    // Allow func to be called if T is the const version of T2
    // e.g., T is 'int const' and T2 is 'int'
    template <typename T2,
              typename = typename std::enable_if<
                                          std::is_same<T, T2 const>::value>::type>
    void func(A<T2> const &)
    {
        std::cout << "Conversion" << std::endl;
    }

    // Allow func to be called for same version of T
    void func(A const &)
    {
        std::cout << "No conversion" << std::endl;
    }
};

int main()
{
    A<int const> a;

    a.func(A<int const>{});

    return 0;
}

Этот код при компиляции с G CC -8.3 компилирует и выдает вывод No conversion - он выбрал версию func, которая не использует std::enable_if. Однако, если я закомментирую вторую версию func, она все равно будет скомпилирована и теперь выдаст вывод Conversion. Другими словами, обе версии func в A могут использоваться для этого метода. Учитывая, что обе перегрузки являются жизнеспособными, какое конкретное правило c используется компилятором для выбора func(A const &) поверх другой версии (func(A<T2> const &))?

Ответы [ 2 ]

7 голосов
/ 21 февраля 2020

Правило состоит в том, что если не функциональный шаблон и специализация функционального шаблона имеют одну и ту же сигнатуру, то не функциональный шаблон выбирается поверх специализации шаблона. Это можно найти в [over.match.best] / 2

С учетом этих определений жизнеспособная функция F1 определена как лучшая функция, чем другая жизнеспособная функция F2, если для всех аргументов i ICSi (F1) не хуже последовательности преобразования, чем ICSi (F2), и тогда

[...]

  • F1 не является специализацией шаблона функции и F2 - специализация шаблона функции [...]
4 голосов
/ 21 февраля 2020

Вы можете прочитать о разрешении перегрузки здесь . В частности,

Если какой-либо кандидат является шаблоном функции, его специализации генерируются с использованием вывода аргумента шаблона, и такие специализации обрабатываются так же, как не шаблонные функции, за исключением случаев, когда в t ie указано иное правила прерывателя.

А затем

Лучшая жизнеспособная функция

Для каждой пары жизнеспособных функций F1 и F2 неявная последовательности преобразования из i-го аргумента в i-й параметр ранжируются, чтобы определить, какой из них лучше [...]

F1 определяется как лучшая функция, чем F2, если неявные преобразования для всех аргументов F1 не хуже, чем неявные преобразования для всех аргументов F2, и [...]

4) [...] F1 - не шаблонная функция, а F2 - шаблонная специализация

В этом простом примере применяются те же правила:

#include<iostream>

template <typename T> 
void foo(T i) { std::cout << "template" ; }

void foo(int i) { std::cout << "non template"; }

int main() {
    foo(1);   // non template
}
...