В чем разница между
template <typename T> void func( T t ) { /* ... */ }
и альтернативу С++ 14, используя lambdas с автоматическими параметрами?
auto func = []( auto t ) { /* ... */ }
Какой из них предпочтительнее?
В чем разница между
template <typename T> void func( T t ) { /* ... */ }
и альтернативу С++ 14, используя lambdas с автоматическими параметрами?
auto func = []( auto t ) { /* ... */ }
Какой из них предпочтительнее?
Первый - это шаблон функции. Он может быть специализированным и перегруженным. Он может быть найден ADL. Если вы хотите принять адрес, вы должны либо явно указать параметры шаблона, либо сделать это в контексте, в котором компилятор может их вывести.
Второй, если предположить, что он отображается в области пространства имен, является глобальным объектом с шаблоном оператора вызова функции. Он не может быть специализированным или перегруженным (глобальные переменные конфликтуют с функциями, они не перегружают их). Он не может быть найден ADL (ADL только находит функции и шаблоны функций). Если вы используете на нем оператор адреса, вы получаете адрес объекта, который довольно бесполезен. Сам объект может быть преобразован в указатель функции, если компилятор может вывести аргументы; вы не можете предоставить их явно.
Вы можете использовать то, что хотите; просто знайте о преимуществах и недостатках любого выбора. Я бы порекомендовал первый. Единственным преимуществом второго является его терпение, и я надеюсь, что мы получим краткий синтаксис для шаблонов функций в не слишком отдаленном будущем.
auto func(auto t) { ... }
Разница в том, что первый - это шаблон функции, который вы должны определить, прежде чем использовать его; как только определение будет там, любой может его использовать. Таким образом, это многократно используемый фрагмент кода и остается там навсегда.
Lambdas, с другой стороны, удобен: вы можете определить его, когда вам это нужно. Если лямбда определена внутри функции, как локальный объект, тогда только эта функция может использовать ее и передавать ее другим функциям. Это все еще многократно, но меньше, чем шаблон функции. Однако lambdas, определенный на уровне пространства имен, является многократно используемым, как шаблон функции, потому что любой может использовать его. Поэтому он не сильно отличается от шаблона функции, когда вы определяете его на уровне пространства имен. Эксперты могут обнаружить некоторые угловые случаи. В одном из таких случаев вы можете специализировать шаблон функции:
//specialization : possible only in case of template!
template<> void func(MyClass obj) { /* ... */ }
Вы не можете сделать это с помощью лямбда!
N3337, [expr.prim.lambda]/3:
Тип лямбда-выражения (который также является типом закрывающий объект) является уникальным, неназванным типом типа ununion, называемым тип закрытия - свойства которого описаны ниже. Этот тип класса не является совокупностью (8.5.1). Тип закрытия объявляется в наименьший объем блока, класс или область пространства имен, которая содержит соответствующее лямбда-выражение.
Этот тип закрытия останется классом. Но его перегруженный оператор вызова функции будет шаблоном операторной функции, позволяющим различные специализации. Кроме того, в отличие от шаблонов функций вы можете неявно преобразовывать объект замыкания в указатель функции. Это действительно удобно, не так ли? Цитируя N3559, он будет выглядеть примерно так:
Для общей лямбда L:
int(*fp)(int, char) = [](auto a, auto b){return a+b;};
Тип замыкания
struct/*anonymous*/
{
template<class A,class B>
auto operator()(A a,B b) const
{
return a+b;
}
private:
template<class A,class B>
static auto __invoke(A a,B b)
{
return a+b;
}
template<class A,class B,class R>
using fptr_t = R(*)(A,B);
public:
template<class A,class B,class R>
operator fptr_t<R,A,B>() const
{
return &__invoke<A,B>; // Fixed that manually, they forgot to qualify the template name
}
} L;
int(*fp)(int,char) = L;
(Будет выполнен обычный вывод аргумента шаблона)