Аргументы создания экземпляров visual studio

Обновлено: 06.07.2024

Универсальные ссылки (то есть «прямые ссылки», c++ стандартное название) и идеальная пересылка в c++11 , c++14 и за ее пределами имеют много важных преимуществ; увидеть Вот , а также Вот .

В статье Скотта Мейерса, упомянутой выше (ссылка на сайт ), как правило, утверждается, что:

Если переменная или параметр объявлены как имеющие тип T&& для некоторых выведенный тип T, эта переменная или параметр является универсальной ссылкой.

Пример 1

Действительно, используя clang ++, мы видим, что следующий фрагмент кода успешно скомпилируется с -std=c++14 :

Дано любое описание универсальных ссылок (прямые ссылки) и вывода типа (см., Например, это объяснение ) понятно, почему вышесказанное работает. Хотя из того же объяснения не совсем понятно, почему нижеприведенное также не работает.

(не удалось) Пример 2

Этот вопрос решает ту же проблему. Однако предоставленные ответы не объясняют, почему шаблонные типы не классифицируются как «выводимые».

То, что я собираюсь показать (по-видимому), удовлетворяет требованиям, изложенным выше Мейерсом. Тем не менее, следующий код снят терпит неудачу компилировать, выдавая ошибку (среди прочего для каждого вызова f ):

В контексте, так как тип T<A> из f параметр является вывел, обязательно объявление параметра T<A>&& t будет вести себя как универсальная ссылка (прямая ссылка).

Пример 3 (для ясности в описании проблемы под рукой)

Позвольте мне подчеркнуть следующее: сбой кода в Example 2 компилировать это не благодаря тому факту, что struct foo<> это шаблонный тип. Похоже, причина неудачи только объявлением f Параметр как шаблонный тип.

Рассмотрим следующую редакцию предыдущего кода, которая сейчас делает компиляции:

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

Вопросы:

Почему второй пример не работает, как ожидалось? Существуют ли способы преодоления этой проблемы с помощью шаблонных типов в c++11/14 ? Существуют ли хорошо известные существующие кодовые базы (в дикой природе), успешно использующие c++ Прямые ссылки с шаблонными типами?

Решение

Когда вы вызываете какую-то функцию f с некоторым значением:

затем f должен быть в состоянии принять такое значение. Это тот случай, когда первый параметр f является (lvalue) ссылочным типом, или когда это вообще не ссылка:

это не будет работать, когда параметр является rvalue ссылкой:

Конечно, здесь слишком много ссылок. Так что C ++ имеет рушатся правила , которые на самом деле довольно просты:

  • T& & становится T&
  • T& && становится T&
  • T&& & становится T&
  • T&& && становится T&&

Благодаря второму правилу, «эффективный» тип первого параметра f является ссылкой на lvalue, так что вы можете привязать ее к lvalue.

Сейчас, T это какой-то шаблон. У вас не может быть ссылки на шаблон.
Ссылка (тип) создается из (возможно, неполного) тип. Так что ни на что, T<A> (который является типом, но нет параметр шаблона, который может быть выведен) не превратится в (lvalue) ссылку, что означает T<A> && не нуждается в свертывании и остается тем, что есть: ссылка на значение. И, конечно, вы не можете привязать lvalue к ссылке на rvalue.

Но если вы передадите ему значение, то даже g буду работать.

Все вышеперечисленное можно увидеть в следующем примере:

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

Затем вы можете извлечь как шаблон, так и тип, с которым был создан экземпляр шаблона внутри функции:

T<A>&& не универсальная ссылка, потому что T<A> не является параметром шаблона. Это (после вычета обоих T а также A простой (фиксированный / не универсальный) тип.

Серьезным подводным камнем для ссылки на пересылку было бы то, что вы больше не можете выразить текущее значение T<A>&& : Rvalue ссылка на некоторый тип, созданный из шаблона T с параметром A ,

Другие решения

Почему второй пример не работает, как ожидалось?

У вас есть две подписи:

f принимает ссылку на пересылку, но f2 не. Конкретное правило, из [temp.deduct.call], выделено жирным шрифтом:

экспедиционная ссылка это значение
ссылка на резюме параметр шаблона. Если P является ссылкой для пересылки, а аргумент является
lvalue, тип «lvalue ссылка на A» используется вместо A для вывода типа.

С f , аргумент является ссылкой на параметр шаблона T ). Но с f2 , T<A> не является параметром шаблона. Таким образом, эта функция просто принимает в качестве аргумента ссылку на значение T<A> , Вызовы не компилируются, потому что все ваши аргументы являются lvalue, и в этом случае нет особых исключений для вызова с lvalue.

Что касается преодоления проблемы, я думаю, что более или менее эквивалентным способом было бы вывести ее с помощью прямой ссылки и инициировать сравнение с T<A> вручную.

Если вы на самом деле не хотите T<A> но определенный тип, лучший способ заключается в использовании std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>> что гораздо проще, я думаю.

Недостаточно иметь вывод типа. Форма объявления типа должна быть именно так T&& (Rvalue ссылка на просто параметр шаблона). Если это не так (или нет вывода типа), параметр является ссылкой на rvalue. Если аргумент является lvalue, он не будет компилироваться. поскольку T<A>&& не имеет этой формы, f (T<A> && t) не может принять lvalue (как ссылку lvalue), и вы получаете ошибку. Если вы думаете, что это требует слишком много общего, считайте, что простой const классификатор тоже ломает:

(оставляя в стороне относительную бесполезность ссылки на постоянное значение)

Правила для свертывания ссылок просто не действуют, если не использовать наиболее общую форму T&& используется. Без способности к f распознать именующий аргумент был передан и обрабатывать параметр как lvalue ссылка, нет ссылки на свертывание, которое должно быть сделано (т.е. свертывание T& && в T& не может случиться, и это просто T<something>&& , r refue ref. на шаблонный тип). Необходимый механизм для функции, чтобы определить, передается ли значение или значение в качестве аргумента, кодируется в выводимом параметре шаблона. Тем не менее, это кодирование происходит только для универсального опорного параметра, так как строго определены.

Должен быть способ явного определения параметра как ссылки на rvalue, чтобы исключить аргументы lvalue. Это рассуждение, по-видимому, применимо к другим типам параметров, включая квалификации cv.

Кроме того, кажется, что есть способы обойти это (см. Черты и SFINAE), но я не могу ответить на эту часть. 🙂

В предыдущем уроке (8.13 – Шаблоны функций) мы представили шаблоны функций и преобразовали обычную функцию max() в шаблон функции max<T> :

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

Использование шаблона функции

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

Чтобы использовать наш шаблон функции max<T> , мы можем вызвать функцию со следующим синтаксисом:

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

Давайте посмотрим на это на простом примере:

Когда компилятор встречает вызов функции max<int>(1, 2) , он определяет, что определение функции для max<int>(int, int) еще не существует. Следовательно, для его создания компилятор будет использовать наш шаблон функции max<T> .

Процесс создания функций (с определенными типами) из шаблонов функций (с шаблонными типами) называется созданием экземпляра шаблона функции (или для краткости созданием экземпляра). Когда этот процесс происходит из-за вызова функции, он называется неявным созданием экземпляра. Созданная функция часто называется экземпляром функции (для краткости – экземпляром) или шаблонной функцией. Экземпляры функций – это обычные во всех отношениях функции.

Процесс создания экземпляра функции прост: компилятор, по сути, клонирует шаблон функции и заменяет шаблонный тип ( T ) фактическим типом, который мы указали ( int ).

Поэтому, когда мы вызываем max<int>(1, 2) , создаваемая функция выглядит примерно так:

Вот тот же пример, что и выше, показывающий, что на самом деле компилирует компилятор после создания всех экземпляров:

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

Приведем еще один пример:

Это работает аналогично предыдущему примеру, но на этот раз наш шаблон функции будет использоваться для генерации двух функций: один раз T заменяется на int , а другой раз T заменяется на double . После создания всех экземпляров программа будет выглядеть примерно так:

Здесь следует отметить еще одну вещь: когда мы создаем экземпляр max<double> , созданная функция имеет параметры типа double . Но поскольку мы предоставили аргументы типа int , эти аргументы будут неявно преобразованы в double .

Вывод аргументов шаблона

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

В этом вызове функции мы указали, что хотим заменить T на int , но мы также вызываем функцию с аргументами int .

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

Например, вместо такого вызова функции:

Вместо этого мы можем сделать одно из следующих действий:

В любом случае компилятор увидит, что мы не предоставили фактический тип, поэтому он попытается вывести фактический тип из аргументов функции, что позволит ему сгенерировать функцию max() , где все параметры шаблона соответствуют типу предоставленных аргументов. В этом примере компилятор сделает вывод, что использование шаблона функции max<T> с фактическим типом int позволяет ему создать экземпляр функции max<int>(int, int) , где тип обоих параметров шаблона ( int ) соответствует типу предоставленных аргументов ( int ).

Разница между этими двумя случаями связана с тем, как компилятор разрешает вызов функции из набора перегруженных функций. В верхнем случае (с пустыми угловыми скобками) компилятор будет учитывать только перегрузки шаблонной функции max<int> при определении того, какую перегруженную функцию вызвать. В нижнем случае (без угловых скобок) компилятор будет учитывать как перегрузки шаблонной функции max<int> , так и перегрузки нешаблонной функции max .

Обратите внимание, как синтаксис последнего вызова выглядит идентично обычному вызову функции! Обычно это предпочтительный синтаксис, используемый при вызове шаблонов функций (и тот, который мы будем использовать по умолчанию в будущих примерах, если иное не требуется).

Лучшая практика

При использовании шаблонов функций отдавайте предпочтение обычному синтаксису вызова функций.

Шаблоны функций с параметрами, не относящимися к шаблону

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

У этого шаблона функции первый параметр шаблонный, а второй параметр фиксируется с типом double . Обратите внимание, что тип возвращаемого значения также может быть любым. В этом случае наша функция всегда будет возвращать значение типа int .

Созданные функции не всегда могут компилироваться

Рассмотрим следующую программу:

Компилятор эффективно скомпилирует и выполнит следующее:

что даст результат:

Но что, если мы попробуем что-то подобное?

Когда компилятор пытается разрешить addOne(hello) , он не найдет не-шаблонной функции для addOne(std::string) , но найдет наш шаблон функции для addOne(T) и определит, что он может сгенерировать из него addOne(std::string) . Таким образом, компилятор сгенерирует и скомпилирует это:

Однако это приведет к ошибке компиляции, потому что x + 1 не имеет смысла, когда x является std::string . Очевидное решение здесь – просто не вызывать addOne() с аргументом типа std::tring .

Обобщенное программирование

Поскольку шаблонные типы могут быть заменены любым фактическим типом, их иногда называют универсальными (обобщенными) типами. А поскольку шаблоны могут быть написаны независимо от конкретных типов, программирование с использованием шаблонов иногда называют обобщенным программированием. В то время как C++ обычно уделяет большое внимание типам и проверке типов, обобщенное программирование, в отличие от него, позволяет нам сосредоточиться на логике алгоритмов и разработке структур данных, не беспокоясь об информации о типах.

Заключение

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

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

Лучшая практика

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

Явное создание экземляра (шаблоны функций)
Допустим есть шаблон template&lt;typename T&gt; void foo(T); . Для чего может понадобиться делать.

Явное создание экземпляра класса и явная специализация шаблона класса
Всем добрый день! Не могу разобраться - эти две технологии дают один и тот же результат? В каких.

Создание шаблонной функции
Создать шаблонную функцию, изменяющий порядок элементов таким образом: первая половина списка.


Создание шаблонной функции сортировки с предикатом
MrGluck, Более менее проясняется. Но мне нельзя пользоваться функциями из библиотек. Нужно написать.

lArtl, да, я знаю об этой возможности. Меня интересует инструкция из 9 строки моего кода.
В книге автор упомянул о ней, но не сказал, для чего она нужна. Сказал лишь, что экземпляр шаблонной функции создастся, даже если и не будет использован.

Решение

Не совсем понимаю, зачем нужно явное создание экземпляров шаблонной функции:

Начиная с С++11 явное создание экземпляров шаблонной функции может использоваться для ускорения компиляции.

В "обычном" режиме использования шаблонов они инстанциируются неявно и независимо в каждой единице трансляции. Таким образом один и тот же шаблон может оказаться инстанциированным огромное количество раз в огромном количестве объектных файлов. Потом со всем этим ворохом идентичных инстанциирований должен разбираться линкер - выкинуть все идентичные инстанциирования, оставив только одно для каждой специализации.

Для часто используемых специализаций можно обойтись без этой бессмысленной генерации идентичных инстанциирований компилятором (с последующим их уничтожением линкером), если в заголовочном файле объявить

и в одном из .cpp файлов сделать явное инстанциирование. В тех единицах трансляции, где включен этот заголовочный файл, компилятор, видя данное объявление, не будет больше инстанциировать данную специализацию шаблона. (Он сочтет это как сигнал, что шаблон уже где-то явно инстанциирован в другом месте.)

Создание специализации шаблонной функции и задача на std::vector
Здравствуйте, подскажите, пожалуйста: 1.В чем ошибка объявления специализации шаблонной.

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

Создание нового экземпляра дочернего класса из экземпляра базового
Всем привет! Извиняюсь, если вопрос глупый, но что-то не смог найти конкретный ответ на него. .


Шаблоны. В чем разница между созданием экземпляра и специализацией?
Протер глаза о Прата, не могу доехать в чем разница между созданием экземпляра и специализацией.

Превью к статье о передаче аргументов командной строки в Visual Studio

Иногда нужно, чтобы программа выполняла разные действия в зависимости от заданных параметров при запуске. Для этого используется механизм аргументов командной строки. Чтобы задать аргументы достаточно написать их в консоли после имени запускаемой программы. Однако при работе в интегрированной среде разработки Visual Studio вы не запускаете программу через консоль самостоятельно, так как этим занимается сама студия. К сожалению, далеко не очевидно, каким образом задавать аргументы командной строки в Visual Studio. Конечно, можно просто открыть консоль, перейти в папку с проектом и запустить приложение прямо оттуда, передав нужные аргументы, однако есть способ сделать это прямо в среде разработки. Так давайте разберёмся ним и научимся передавать аргументы прямо из студии, не запуская консоль и не ища расположение запускаемого проекта.

Шаг первый. Создаём новый С++ проект

Если вы не знаете, как создать новый проект, прочитайте нашу статью об этом: Как создать C/C++ проект в Visual Studio.

Создание проекта в VS

Студия создаст проект и открое редактор кода, в котором будет примерно следующее:

Шаг второй. Пишем код для отображения переданных аргументов

Запустим приложение и убедимся, что ни одного аргумента помимо имени программы передано не было:

Запуск приложения

Шаг третий. Задание аргументов командной строки

Чтобы передать аргументы командной строки в Visual Studio нужно:

  1. Выбрать в верхнем меню панель Проект;
  2. В появившейся вкладке выбрать Свойства;
  3. В открывшемся окне выбрать Отладка
  4. В правой части задать аргументы в поле Аргументы команды

Проект

Свойства

Отладка

Напишем там какие-нибудь аргументы для проверки и ещё раз запустим приложение

Повторный запуск приложения

Программа вывела все аргументы, которые только что были переданы. А значит мы научились задавать параметры командной строки прямо в Visual Studio.

Фото Перминова Андрея, автора этой статьи

Выпускник МГУ им. М.В. Ломоносова

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

Читайте также: