Вызов функции шаблона без ‹›; вывод типа

Если у меня есть шаблон функции с typename T, где компилятор может сам установить тип, мне не нужно явно писать тип, когда я вызываю функцию, например:

template < typename T > 
T min( T v1, T v2 ) {
   return ( v1 < v2 ) ? v1: v2;
}
int i1 = 1, i2 = 2; int i3 = min( i1, i2 ); //no explicit <type> 

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

template < typename TOut, typename TIn >
TOut round( TIn v ) {
   return (TOut)( v + 0.5 );
}
double d = 1.54;
int i = round<int>(d); //explicit <int>

Это правда, что я всегда должен указывать хотя бы одно имя типа? Я предполагаю, что причина в том, что C ++ не может различать функции между разными типами возврата.

Но если я использую функцию void и передаю ссылку, я снова не должен явно указывать возвращаемое имя типа:

template < typename TOut, typename TIn > 
void round( TOut & vret, TIn vin ) {
   vret = (TOut)(vin + 0.5);
}
   double d = 1.54;
   int i; round(i, d); //no explicit <int>

Следует ли делать вывод о том, чтобы избегать функций с возвратом и больше отдавать предпочтение void функциям, которые возвращаются по ссылке при написании шаблонов? Или есть возможность избежать явной записи типа возврата? Что-то вроде "вывода типа" для шаблонов. Возможен ли «вывод типа» в C ++ 0x?


person OlimilOops    schedule 14.05.2010    source источник
comment
Преобразование между типами делает идею вывода типов громоздкой, поэтому вы не можете перегрузить возвращаемые типы и должны указывать ее, когда это параметр шаблона.   -  person Francesco    schedule 14.05.2010
comment
Возможно, вы захотите поработать над своим алгоритмом округления. Что должно получиться -1,54? И: что, если вы хотите получить округленное значение double?   -  person UncleBens    schedule 14.05.2010


Ответы (3)


Разрешение перегрузки выполняется только на основе аргументов функции; возвращаемое значение вообще не используется. Если тип возвращаемого значения не может быть определен на основе аргументов, вам придется указать его явно.

Я бы не стал «возвращать» значение через ссылочный параметр; что делает неясным код вызова. Например, я бы предпочел это:

double x = round<double>(y);

через это:

double x;
round(x, y);

потому что в последнем случае легко спутать ввод и вывод, и совсем не ясно, что x модифицируется.

В конкретном случае round вам, вероятно, в любом случае понадобится только один или два типа для TOut, поэтому вы можете просто оставить этот аргумент шаблона:

template<typename TIn>
int roundToInt(TIn v) {
    return (int)(v + 0.5);
}

Я нахожу roundToInt(x) немного яснее, чем round<int>(x), потому что ясно, для чего используется тип int.

person Thomas    schedule 14.05.2010
comment
По поводу ясности: round_to<int>(x)? ;) - person UncleBens; 14.05.2010
comment
@UncleBens, я бы сказал, что roundToInt(x) яснее, чем roundTo<int>(x), который намного понятнее, чем round<int>(x). - person Gabriel Staples; 06.04.2020
comment
При этом roundTo<int>(x), безусловно, является хорошей альтернативой, если вы хотите сохранить преимущество написания шаблонной функции один раз, а не писать отдельную функцию для каждого типа вывода для округления, при этом сохраняя возможность округления до множества типов. . Но в большинстве ситуаций, когда вы в любом случае округляетесь только до нескольких типов, я думаю, что явность roundToInt(x) лучше, чем позволить кому-то округлить что-либо, поэтому я бы выбрал roundToInt(x) (плюс определил еще один или два, следуя этому шаблону), если только Мне нужно дать кому-нибудь возможность перейти к практически любому типу. - person Gabriel Staples; 06.04.2020

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

Нет почему? Что вы получаете? Только вывод типа (чтобы писать меньше кода). Но вы теряете гораздо более логичный синтаксис присвоения значения (и, следовательно, больше кода для написания). Итак, одно получилось, другое потеряно. Я не вижу в этом никакой пользы.

Может даже помочь явное указание типа шаблона: рассмотрим случай lexical_cast. Если не указать тип возвращаемого шаблона, это может сбить с толку.

person Konrad Rudolph    schedule 14.05.2010

Позвольте мне добавить к тому, что говорили другие, сказав, что вам следует предпочесть приведение типов C ++ над приведением типов в стиле C.

vret = (TOut)(vin + 0.5);

против

vret = static_cast<TOut>(vin + 0.5);

статическое приведение всегда будет терпеть неудачу, если вы попытаетесь преобразовать несвязанные типы. Это может помочь в отладке.

person wheaties    schedule 14.05.2010
comment
И у вас также будут предупреждения / ошибки во время компиляции, что намного лучше, чем обнаружение во время выполнения. - person Matthieu M.; 14.05.2010
comment
Какими должны быть задействованные типы, чтобы static_cast давал другой результат, чем приведение в стиле C в этом шаблоне? (Да, в служебной функции вы можете стремиться к хорошему стилю, но я не вижу такой большой выгоды в чистом математическом коде, выполняющем преобразование между числовыми типами - чтобы гарантировать публикацию, а не, возможно, комментарий.) - person UncleBens; 14.05.2010
comment
Возможно, вы читали этот пост, но мне пришлось вмешаться. Если вы используете static_cast, ошибка означает, что объект / исполняемый файл даже не может существовать; тогда верно обратное, то есть если у вас есть объект / исполняемый файл, вы знаете, что static_cast был допустимым между типами. По мере продвижения к метапрограммированию шаблонов вам потребуется больше контроля над типами в их деревьях наследования, например в CRTP. - person John P; 30.06.2018