Информация об изменениях

Сообщение Re[3]: Template && reference от 13.02.2022 21:30

Изменено 13.02.2022 21:39 Андрей Тарасевич

Re[3]: Template && reference
Здравствуйте, AnatolyDu, Вы писали:

АТ>>Все не так. В С++ шаблонах, если некий дедуцируемый шаблонный параметр `T` можно успешно дедуцировать из нескольких аргументов функции, то все результаты таких дедукций должны точно совпадать. Малейшее несовпадение является ошибкой. В вашем случае в варианте `fCaller( &fReference, strParam )` шаблонный параметр `TArguments` можно дедуцировать и из первого параметра функции, и из второго. Дедукция из первого дает `const std::string &`, а из второго — просто `std::string`. Это — разные типы. Получаем неоднозначную дедукцию.


AD>А почему из второго аргумента выводится std::string, а не const std::string& ?

AD>Ведь он имеет явный тип const std::string& — const std::string &strParam ?

Здесь работает два момента:

1. Аргумент `strParam`, который вы передаете в вызов — это выражение. Правила обработки выражений в С++ говорят, что если выражение имеет ссылочный тип, то этот ссылочный тип немедленно теряется и все дальнейшее рассмотрение делается для нессылочного типа: http://eel.is/c++draft/expr.type#1

То есть согласно этому пункту ваш тип `const std::string &` сразу превращается в тип `const std::string`

2. Далее, в описании процесса дедукции шаблонных аргументов говорится, что для нессылочных параметров функции верхние cv-квалификаторы ее аргументов игнорируются: http://eel.is/c++draft/temp.deduct.call#2.3

Согласно этому пункту для целей дедукции ваш тип `const std::string` превращается в тип `std::string`.

По этой причине нет никакой разницы, указывать ли в качестве аргумента `strString` или `strParam` — дедукция в обоих случаях дедуцирует один и тот же тип. Как часто говорят, именованную ссылку в языке С++ можно рассматривать как альтернативное имя для того же самого объекта. Язык С++ специально предпринимает усилия для того, чтобы скрыть различия между именованным объектом и именованной ссылкой на этот же объект. Оба способа доступа ведут себя внешне одинаково.

AD>>>Хочу написать функцию, получающую другую функцию( указатель на неё ) и её аргументы (разного количества и типов) :


АТ>>Второй вопрос, возникающий в таких случаях: если уж вы зачем-то хотите, чтобы ваш параметр был именно указателем на функцию — ваше право. В такой ситуации для того, чтобы получше воспроизвести "натуральное" поведение функций можно попробовать намеренно поместить типы параметров в non-deduced context, то есть умышленно подавить дедукцию шаблонных параметров из `arguments`


АТ>>
АТ>>// C++20
АТ>>template<typename... TArguments>
АТ>>void fCaller( FunctionToCall<TArguments...> a_FuncToCall, std::type_identity_t<TArguments>... arguments)
АТ>>{
АТ>>  a_FuncToCall( arguments... );
АТ>>}
АТ>>


AD>У меня пока что не C++20


C++20 тут чисто номинально — для `std::type_identity_t`. Никто вам даже в С++98 не мешает написать самостоятельно

template <typename T>
struct non_deduced
{
  typedef T type;
};


и далее использовать `typename non_deduced<...>::type` вместо `std::type_identity_t<...>`.
Re[3]: Template && reference
Здравствуйте, AnatolyDu, Вы писали:

АТ>>Все не так. В С++ шаблонах, если некий дедуцируемый шаблонный параметр `T` можно успешно дедуцировать из нескольких аргументов функции, то все результаты таких дедукций должны точно совпадать. Малейшее несовпадение является ошибкой. В вашем случае в варианте `fCaller( &fReference, strParam )` шаблонный параметр `TArguments` можно дедуцировать и из первого параметра функции, и из второго. Дедукция из первого дает `const std::string &`, а из второго — просто `std::string`. Это — разные типы. Получаем неоднозначную дедукцию.


AD>А почему из второго аргумента выводится std::string, а не const std::string& ?

AD>Ведь он имеет явный тип const std::string& — const std::string &strParam ?

Здесь работает два момента:

1. Аргумент `strParam`, который вы передаете в вызов — это выражение. Правила обработки выражений в С++ говорят, что если выражение имеет ссылочный тип, то этот ссылочный тип немедленно теряется и все дальнейшее рассмотрение делается для нессылочного типа: http://eel.is/c++draft/expr.type#1

То есть согласно этому пункту ваш тип `const std::string &` сразу превращается в тип `const std::string`

2. Далее, в описании процесса дедукции шаблонных аргументов говорится, что для нессылочных параметров функции верхние cv-квалификаторы ее аргументов игнорируются: http://eel.is/c++draft/temp.deduct.call#2.3

Согласно этому пункту для целей дедукции ваш тип `const std::string` превращается в тип `std::string`.

По этой причине нет никакой разницы, указывать ли в качестве аргумента `strString` или `strParam` — дедукция в обоих случаях дедуцирует один и тот же тип `std::string`. Как часто говорят, именованную ссылку в языке С++ можно рассматривать как альтернативное имя для того же самого объекта. Язык С++ специально предпринимает усилия для того, чтобы скрыть различия между именованным объектом и именованной ссылкой на этот же объект. Оба способа доступа ведут себя внешне одинаково.

AD>>>Хочу написать функцию, получающую другую функцию( указатель на неё ) и её аргументы (разного количества и типов) :


АТ>>Второй вопрос, возникающий в таких случаях: если уж вы зачем-то хотите, чтобы ваш параметр был именно указателем на функцию — ваше право. В такой ситуации для того, чтобы получше воспроизвести "натуральное" поведение функций можно попробовать намеренно поместить типы параметров в non-deduced context, то есть умышленно подавить дедукцию шаблонных параметров из `arguments`


АТ>>
АТ>>// C++20
АТ>>template<typename... TArguments>
АТ>>void fCaller( FunctionToCall<TArguments...> a_FuncToCall, std::type_identity_t<TArguments>... arguments)
АТ>>{
АТ>>  a_FuncToCall( arguments... );
АТ>>}
АТ>>


AD>У меня пока что не C++20


C++20 тут чисто номинально — для `std::type_identity_t`. Никто вам даже в С++98 не мешает написать самостоятельно

template <typename T>
struct non_deduced
{
  typedef T type;
};


и далее использовать `typename non_deduced<...>::type` вместо `std::type_identity_t<...>`.