Информативное использование C 11 decltype

#c #templates #c 11 #decltype

#c #шаблоны #c 11 #decltype

Вопрос:

Я только что видел этот действительно хороший доклад Бориса Джейбса Rock Hard: C Evolving. В разделе доклада, посвященном универсальному программированию более высокого порядка, он говорит, что ниже приведен пример функции, которая является более универсальной в отношении ее возвращаемого типа и приводит к меньшим перегрузкам шаблонных функций

 template <typename Func>
auto deduce(const Func amp; f) -> decltype(f())
{..}
  

Это, однако, может быть реализовано с использованием простого синтаксиса шаблона следующим образом

 template <typename Func>
Func deduce(const Func amp; f)
{..}
  

поэтому я думаю, что выбранный пример на самом деле не показывает уникальную силу decltype . Может кто-нибудь привести пример такого более поучительного использования decltype ?

Комментарии:

1. Нет. Первый пример означает, что deduce<Func>(f) возвращает тип Func результата. Второй пример означает, что deduce<Func>(f) возвращает Func . Вы видите разницу?

2. Ааа, извините, я пропустил лишнее () внутри аргумента to decltype . Моя ошибка.

Ответ №1:

Ваши подозрения неверны.

 void f() { }
  

Теперь deduce(amp;f) имеет тип void , но при вашей перезаписи у него есть тип void(*)() . В любом случае, везде, где вы хотите получить тип выражения или объявления, вы используете decltype (обратите внимание на тонкую разницу между этими двумя. decltype(x) не обязательно совпадает с decltype((x)) ).

Например, вполне вероятно, что ваша реализация стандартной библиотеки где-то содержит такие строки, как

 using size_t = decltype(sizeof(0));
using ptrdiff_t = decltype((int*)0 - (int*)0);
using nullptr_t = decltype(nullptr);
  

Определение правильного возвращаемого типа add было сложной проблемой на протяжении всего прошлого C . Теперь это простое упражнение.

 template<typename A, typename B> 
auto add(A constamp; a, B constamp; b) -> decltype(a   b) { return a   b; }
  

Мало что известно о том, что вы можете использовать decltype до :: и в имени псевдодеструктора

 // has no effect
(0).~decltype(0)();

// it and ite will be iterators into an initializer list 
auto x = { 1, 2, 3 };
decltype(x)::iterator it = x.begin(), ite = x.end();
  

Комментарии:

1. Не могли бы вы, пожалуйста, немного объяснить это: (0).~decltype(0)(); ? Боюсь, я этого не понимаю.

2. (0).~decltype(0)(); не компилируется с gcc, но компилируется с clang . Полезно, если 0 заменяется объектом класса, это будет явным вызовом собственного деструктора объекта. Так что это допустимо (по крайней мере, с помощью clang), чтобы шаблон мог работать с объектами класса, а также с int и т. Д. См. godbolt.org/z/M4NNfN

Ответ №2:

 std::for_each(c.begin(), c.end(), [](decltype (c.front()) val){val*=2;});
  

Автоматическое преобразование value_type контейнера c не может быть выполнено без decltype .

Комментарии:

1. Это невозможно сделать, только если вы используете лямбды, учитывая, что они не являются полиморфными (тем не менее, будем надеяться, что они есть в C y). Если вы использовали объект полиморфной функции, он будет выведен автоматически.

Ответ №3:

Одно место, где я его использую, — это то, где мне нужно создать переменную, которая должна иметь тот же тип, что и другая переменная. но я не уверен, останется ли в будущем тип тем же или нет.

 void foo(int a)//maybe in future type of a changed
{
    decltype(a) b;
    //do something with b
}