forward
выражает намерение, и его может быть безопаснее использовать, чем static_cast
: static_cast
рассматривает преобразование, но некоторые опасные и предположительно неумышленные преобразования обнаруживаются с forward
:
struct A{
A(int);
};
template<class Arg1,class Arg2>
Arg1&& f(Arg1&& a1,Arg2&& a2){
return static_cast<Arg1&&>(a2); // typing error: a1=>a2
}
template<class Arg1,class Arg2>
Arg1&& g(Arg1&& a1,Arg2&& a2){
return forward<Arg1>(a2); // typing error: a1=>a2
}
void test(const A a,int i){
const A& x = f(a,i);//dangling reference
const A& y = g(a,i);//compilation error
}
Пример сообщения об ошибке: ссылка на проводник компилятора
Как применяется это обоснование: Как правило, это оправдывается тем, что использование static_cast позволяет избежать ненужного создания экземпляра шаблона.
Является ли время компиляции более проблематичным, чем возможность сопровождения кода? Должен ли кодер даже терять время, рассматривая возможность минимизации «ненужного создания шаблона» в каждой строке кода?
Когда создается экземпляр шаблона, его создание вызывает создание экземпляров шаблона, которые используются в его определении и объявлении. Так что, например, если у вас есть функция:
template<class T> void foo(T i){
foo_1(i),foo_2(i),foo_3(i);
}
, где foo_1
, foo_2
, foo_3
- шаблоны, создание экземпляра foo
вызовет 3 экземпляра. Затем рекурсивно, если эти функции вызывают создание экземпляров 3 других шаблонных функций, вы можете получить, например, 3 * 3 = 9 экземпляров. Таким образом, вы можете рассматривать эту цепочку создания экземпляров как дерево, в котором создание экземпляров корневой функции может вызывать тысячи экземпляров как экспоненциально растущий волновой эффект. С другой стороны, функция, подобная forward
, является листом в этом дереве реализации. Поэтому, избегая его создания, можно избежать только 1 создания.
Таким образом, лучший способ избежать взрыва экземпляров шаблона - это использовать динамический полиморфизм для «корневых» классов и тип аргумента «корневых» функций, а затем использовать статический полиморфизм только для критичных ко времени функций, которые фактически находятся выше в этом дереве создания. .
Так что, на мой взгляд, использование static_cast
вместо forward
во избежание создания экземпляров - это потеря времени по сравнению с преимуществом использования более выразительного (и более безопасного) кода. Взрыв экземпляра шаблона более эффективно управляется на уровне архитектуры кода.