С этой фичей есть одна тонкость.
Поясню на примере.
Казалось бы, в чем разница между двумя следующими декларациями?
template <class T>
auto Add(T a,T b) { return a+b; }
template <class T>
auto Add(T a,T b) -> dectype( a+b ) { return a+b; }
А вот в чем. Когда вы навешиваете явный тип через decltype, то вы фактически добавляете неявный концепт к определению функции. Т.е. если вывод типа при подстановке аргументов шаблона провалится, то функция будет молча отброшена. А в первом примере -- нет. Иногда это важно (если есть семейство перегруженных шаблонов).
Здравствуйте, Шахтер, Вы писали:
Ш>А вот в чем. Когда вы навешиваете явный тип через decltype, то вы фактически добавляете неявный концепт к определению функции. Т.е. если вывод типа при подстановке аргументов шаблона провалится, то функция будет молча отброшена. А в первом примере -- нет. Иногда это важно (если есть семейство перегруженных шаблонов).
Если очень надо сделать так, чтобы и decltype был, и SFINAE не вмешивалось, то можно прибегнуть к такой загогулине: сломать SFINAE
class failure {}; // нечто, непригодное к использованию
// типы для foo
struct nonplusable {};
struct nonplusable2 {};
// вывод типа для первой перегрузки: всегда успешный, но иногда болезненный
template<class T> auto foo_1_hint(T x) -> decltype(x+x);
failure foo_1_hint(...); // если эту ветку убрать, то SFINAE опять включится
// перегрузки foo
template<class T> auto foo(T x) -> decltype(foo_1_hint(x)) { cout << "foo1"; return x+x; }
void foo(nonplusable2) { cout << "foo2"; }
void foo(...) { cout << "foo3"; }
int main()
{
foo(1); // foo1
foo(nonplusable()); // foo1 failure вместо случайного попадания в foo3
foo(nonplusable2()); // foo2
}