Ошибка замены с `std::function` и ранее выведенным параметром шаблона - почему?

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

template <typename> 
struct S { };

void g(S<int> t);

template <typename T>
void f(T, std::function<void(S<T>)>);

При попытке вызвать

f(0, g);

Я получаю следующую ошибку:

error: no matching function for call to 'f'
    f(0, g);
    ^

note: candidate template ignored: could not match 
      'function<void (S<type-parameter-0-0>)>' 
      against 'void (*)(S<int>)'
void f(T, std::function<void(S<T>)>);
     ^

живой пример на godbolt.org

Хотя я понимаю, что вообще тип std::function параметр не может быть выведен, так как это невыведенный контекст

В этом случае T можно сначала вывести по переданному аргументу 0, а затем заменен на std::function<void(S<T>)> получить std::function<void(S<int>)>.

Я ожидал, что после вывода T=int, компилятор заменит T везде в подписи, а затем попытайтесь построить std::function параметр с аргументом g.

Почему это не так? Я предполагаю, что порядок, в котором происходит замена / вычитание, как-то связан с этим, но я бы хотел увидеть соответствующую формулировку Стандарта.

Дополнительный вопрос: это то, что потенциально может быть изменено в будущем Стандарте с сохранением обратной совместимости, или есть фундаментальная причина, по которой такая замена не работает?

2 ответа

Решение

Хотя я понимаю, что обычно тип параметра std::function не может быть выведен, поскольку это невыведенный контекст.

Это не невыведенный контекст. Наоборот. Поскольку вычет по параметруstd::functionпредпринимается попытка, но аргумент не являетсяstd::function, удержание не выполняется. Выведение аргументов шаблона из аргументов функции должно согласовываться для всех аргументов функции. Если что-то не удастся, то потерпит неудачу полностью.

[temp.deduct.type]

2 В некоторых случаях выведение выполняется с использованием единственного набора типов P и A, в других случаях будет набор соответствующих типов P и A. Выведение типа выполняется независимо для каждой пары P/A, и выводится значения аргументов шаблона затем объединяются. Если вывод типа не может быть выполнен для какой-либо пары P/A, или если для любой пары вывод приводит к более чем одному возможному набору выводимых значений, или если разные пары дают разные выведенные значения, или если какой-либо аргумент шаблона не остается ни выведенным, ни явным указано, вывести аргумент шаблона не удается.

Превращение типа второго параметра функции в невыведенный контекст - вот как можно преодолеть ошибку.

#include <functional>

template<typename T>
struct type_identity {
    using type = T;
};

template <typename> 
struct S { };

void g(S<int> ) {}

template <typename T>
void f(T, typename type_identity<std::function<void(S<T>)>>::type) {}

int main() {
    f(0, g);
}

Tуспешно выводится из первого аргумента функции, и выводить нечего. Таким образом, посвящение считается успешным.

Жить

Хотя я понимаю, что вообще тип std::function параметр не может быть выведен, поскольку это невыведенный контекст, в данном случае T можно сначала вывести по переданному аргументу 0.

Это неправда. Tвыводится в этом контексте. Если вы измените код на

template <typename T>
void f(std::function<void(S<T>)>);

int main()
{
    f(std::function<void(S<int>)>(g));
}

код будет компилироваться и T правильно выводится.

Ваша проблема в том, что вы передаете объект функции, который она не может извлечь Tот. Компилятор не будет преобразовывать аргументы функции, когда он пытается вывестиT. Это означает, что у вас естьintи функция как типы, переданные в функцию. Он получаетint от 0, затем пытается получить тип из std::function вы передаете второй параметр, но поскольку вы не прошли std::function он не может извлечь T и из-за этого вы получите ошибку.

Другие вопросы по тегам