2010-04-11 2 views
13

У меня есть что-то вроде следующего кода:Как правильно использовать ссылки с переменным числом шаблонов

template<typename T1, typename T2, typename T3, typename T4> 
    void inc(T1& t1, T2& t2, T3& t3, T4& t4) { ++t1; ++t2; ++t3; ++t4; } 

    template<typename T1, typename T2, typename T3> 
    void inc(T1& t1, T2& t2, T3& t3) { ++t1; ++t2; ++t3; } 

    template<typename T1, typename T2> 
    void inc(T1& t1, T2& t2) { ++t1; ++t2; } 

    template<typename T1> 
    void inc(T1& t1) { ++t1; } 

я хотел бы переопределять его, используя предложенные VARIADIC шаблоны из будущего стандарта. Однако все примеры, которые я видел до сих пор в Интернете, похоже, являются примерами printf, разница здесь, по-видимому, заключается в использовании ссылок. Я придумал следующий:

inline void inc() { } 

template<typename T> 
inline void inc(T&& t) { ++t; } 

template<typename T,typename ... Args> 
inline void inc(T&& t, Args&& ... args) { ++t; inc(args...); } 

Что я хотел бы знать:

  • Должен ли я при помощи R-значение вместо ссылок?

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

  • Какие гарантии предлагает новый предложенный стандарт в отношении проблемы рекурсивных вызовов функций, есть ли некоторые признаки того, что вышеупомянутая вариационная версия будет такой же оптимальной, как и оригинал? (Я должен добавить встроенный или некоторые, такие?)

+7

Из любопытства я бросил эти функции в gcc4.5, и рекурсия была удалена, и все это получилось вложенным. –

+0

'T &&' не является ссылкой на rvalue, потому что он успешно выполняет параметр шаблона (в данном случае 'T'). Поэтому его универсальная справочная/переадресационная ссылка, которая привязывается ко всему. То же самое верно для 'Arg &&'. См. [Подробно] (http://en.cppreference.com/w/cpp/language/reference#Forwarding_references). –

ответ

12

я не использовал бы RValue ссылки здесь, потому что позволит вам связываться с rvalues, которые могут позволить такой бессмысленный код, как:

inc(1); 

Таким образом, я бы придерживаться регулярных ссылок:

template<typename T> 
void inc(T& t) { ++t; } 

template<typename T,typename ... Args> 
void inc(T& t, Args& ... args) { ++t; inc(args...); } 
+2

@ Hippicoder: Я думаю, вы неправильно поняли R Samuel. Он сказал: «Я бы не использовал ссылки rvalue». – sellibitze

+1

@sellibitze: Я вижу, поэтому вкратце rvalue refs не добавит ничего особенного в этот образец кода - не сделает его быстрее или медленнее. – Hippicoder

+3

Поскольку пакет параметров может совпадать с пустым списком, эти перегрузки неоднозначны в случае одного аргумента. Вы должны использовать 'void inc() {}' в качестве базового случая и, вероятно, лучше использовать специализацию вместо перегрузки. – Potatoswatter

2

Должен ли я при помощи R-значения вместо ссылок?

Вы имеете в виду ссылки на rvalue? Нет, я не вижу причин для этого.

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

Вы уже здесь. Ваш код должен делать то, что вы хотите.

Какие гарантии дает новый предложенный стандарт по вопросу о вызовах рекурсивных функций, есть ли некоторые признаки того, что вышеупомянутая вариационная версия будет такой же оптимальной, как и оригинал? (должен ли я добавить встроенный или какой-нибудь?)

Стандарт C++ не гарантирует встраивания. Вы можете проверить, что генерирует компилятор. Если вы хотите, чтобы все было встроено - в том числе самый высокий inc-call - вы могли установить встроенную функцию для обеих функций в качестве запроса . Если вы хотите что-то вроде вашего, не VARIADIC шаблона, вы можете обернуть его так:

inline void inc_impl() {} 

template<typename T, typename...U> 
inline void inc_impl(T& t, U&...u) { ++t; inc_impl(u...); } 

template<typename...T> 
void inc(T&...t) { inc_impl(t...); } 

Теперь вкло не встраивать в то время как каждый из его реализаций, вероятно, содержит никакой реальной функции не вызывает, когда встраивание из inc_impl звонков сделать - но опять же, нет никакой гарантии.

+6

'inline' на самом деле не будет иметь никакого влияния на inlining. – GManNickG

+1

@sellibitze: Предполагая, что стандарт не дает никаких таких гарантий, было бы разумным предположить, что компетентные компиляторы при нормальных обстоятельствах оптимизируют такой фрагмент кода в ряд встроенных инков, например, что делает исходная версия перегрузки. – Hippicoder

+3

@ Hippicoder: Да, я бы так подумал. Единственными местами, где обычно требуется ключевое слово 'inline', являются нешаблонные функции, которые предполагается использовать в нескольких единицах перевода, поскольку inline позволяет вам вводить определение в каждый TU, который упрощает прошивку, поскольку он не требует ссылки, оптимизация времени. – sellibitze

1

Какие гарантии дает новый предложенный стандарт по поводу вызовов рекурсивных функций, есть ли некоторые указания на то, что вышеупомянутая вариационная версия будет такой же оптимальной, как и оригинал? (следует ли добавить встроенный или какой-нибудь?)

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

Фактически ключевое слово inline является лишь подсказкой для компилятора, который часто игнорируется, потому что компилятор может решить лучше.

Наконец, g ++ - 4.5 полностью встроено все функции inc на -O2. Не знаю, хотите ли вы этого.

+1

@KennyTM: inlining - это в значительной степени то, что я искал, я надеялся уточнить, что основные компиляторы (msvc, intel и gcc) будут включать вызовы, такие как оригинальные перегрузки. – Hippicoder