Вывод аргумента шаблона функции (класс или шаблон функции) - PullRequest
0 голосов
/ 26 июня 2018

Не могли бы вы помочь мне понять, почему вывод аргумента работает для шаблона класса и не работает для шаблона функции?

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

Но я не понимаю, почему компилятор не может создать определение функции и затем применить неявное приведение?

#include <functional>

template<typename ...ARGS>
class Test1
{
public:
    void add(const std::function<void(ARGS...)>&) {}
};

class Test2
{
public:
    template<typename ...ARGS>
    void add(const std::function<void(ARGS...)>&) {}
};

void func(int) {}

int main()
{
    Test1<int> test1;
    test1.add(func);

    Test2 test2;
    test2.add<int>(func);
}

Ошибка:

В функции 'int main ()':

25:24: ошибка: нет соответствующей функции для вызова 'Test2 :: add (void (&) (int))'

25:24: примечание: кандидат:

14:10: примечание: шаблон void Test2 :: add (const std :: function &)

14:10: примечание: не удалось вывести / заменить аргумент шаблона:

25:24: примечание: несовпадающие типы 'const std :: function' и 'void (int)'

Ответы [ 2 ]

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

Мои первоначальные рассуждения о том, почему следующие фрагменты работают неправильно, но, поскольку @NathanOliver помог мне (см. Ниже), вот пересмотренное объяснение: Во время вывода аргументов шаблона преобразования типов не выполняются. Передача указателя на функцию, которая принимает аргумент std::function, требует такого преобразования. Чтобы обойти эту проблему, вы можете вызвать метод, подобный этому

test2.add(std::function<void(int)>(func));

или измените определение Test2 на

class Test2
{
    template<typename ...ARGS>
    void add(void(*)(ARGS...)) {}
}

, который работает вместе с исходным вызовом

test2.add<int>(func);

В обоих примерах преобразование не требуется. Вызов Test1::add сработал, потому что перед вызовом метода был выполнен вывод типа шаблона, следовательно, преобразование может иметь место.

Обратите внимание, что та же проблема возникает, когда Test2 объявлен с одним единственным параметром шаблона,

class Test2
{
    template<typename T>
    void add(const std::function<void(T)>&) {}
}

со следующими вариантами использования на стороне вызывающего абонента:

test2.add<int>(func); // Conversion ok, function template specified
test2.add(std::function<void(int)>(func)); // Type deduction, no conversion
test2.add(func); // Error, conversion AND type deduction
0 голосов
/ 26 июня 2018

В первом случае вы явно создаете экземпляр шаблона класса Test1. Это означает, что объявление функции для его члена add генерируется с подписью add(const std::function<void(int)>&). Когда компилятор впоследствии пытается разрешить test1.add(func), существует только один этот кандидат. Поскольку std::function<void(int)> может быть неявно создан из функции void(int), сигнатуры совпадают, компилятор просто создает определение функции-члена, и все хорошо.

Во втором случае компилятор должен выполнить вывод / замену аргумента шаблона, чтобы посмотреть, сможет ли он «использовать» шаблон add. Вы можете подумать, что указание int приведет к сглаживанию параметров шаблона, так что вычитание не требуется, но это не так: возможно, вы подразумеваете частичное указание аргументов шаблона, см., Например, здесь, Другими словами, вы можете пытаться создать экземпляр шаблона функции с большим количеством параметров, чем указано явно, по крайней мере, компилятор не знает, если вы это делаете. Таким образом, он все еще должен попытаться сопоставить типы std::function<void(ARGS...)> (или, точнее, std::function<void(int, ...)> и void(int), что не может, потому что неявные преобразования не учитываются для вычета.

Вкратце: задание явных аргументов шаблона не препятствует выводу параметров шаблона для шаблонов функций с переменными числами.

Примечание: я не на 100% тверд с точной терминологией, любой языковой адвокат, исправляющий меня, приветствуется!

Редактировать: Я основываю это прежде всего на том, что читаю здесь .

...