Вариадические шаблоны с перегрузкой параметра 'const'
Сокращенный пример кода:
#include <iostream>
template<typename T>
void func(T &x)
{
std::cout << "non-const " << x << std::endl;
}
template<typename T>
void func(const T &x)
{
std::cout << "const " << x << std::endl;
}
template<typename ...ARGS>
void proxy(ARGS ...args)
{
func(args...);
}
int main()
{
int i = 3;
func(i);
func(5);
func("blah");
proxy(i);
proxy(5);
proxy("blah");
}
Ожидаемый результат:
non-const 3
const 5
const blah
non-const 3
const 5
const blah
Фактический выход:
non-const 3
const 5
const blah
non-const 3
non-const 5
non-const blah
Итак, как-то отделитель const
от параметра функции теряется при переносе вариационного шаблона. Зачем? Как я могу предотвратить это?
PS: проверено с помощью GCC 4.5.1 и SUSE 11.4
Ответы
Ответ 1
Вы просто натыкаетесь на проблему пересылки. Эта проблема решается с помощью совершенной переадресации.
В принципе, вам нужно взять ваши параметры с помощью rvalue-reference и полагаться на std::forward
, чтобы правильно пересылать их, сохраняя при этом их характер:
template<typename ...Args>
void proxy(Args&& ...args)
{
func(std::forward<Args>(args)...);
}
Ответ 2
Как уже упоминал Люк, это проблема переадресации, и ответ на вопрос о том, как предотвратить ее, - это использовать совершенную пересылку. Но я постараюсь ответить на другие вопросы в конце:
Итак, каким-то образом определитель const параметра функции теряется при переносе вариационного шаблона. Почему?
Это имеет все, что связано с типом вывода. Игнорируйте, что вы используете вариативные шаблоны и рассматриваете простейший шаблон одного аргумента:
template <typename T>
void one_arg_proxy( T arg ) {
func( arg );
}
В месте вызова у вас есть one_arg_proxy( 5 )
, то есть аргумент int
rvalue. Вывод типа выводит, чтобы выяснить, что должен быть тип T
, а в правилах указано, что T
- int
, поэтому вызов переводится на one_arg_proxy<int>(5)
, и создание шаблона, который компилируется, составляет:
template <>
void one_arg_proxy<int>( int arg ) {
func( arg );
}
Теперь вызов func
принимает аргумент lvalue, и, следовательно, версия func
, принимающая неконстантную ссылку, является лучшим совпадением (без необходимости конверсии), чем тот, который принимает const&
, что дает результат что вы получаете. Проблема здесь в том, что func
не вызывается с аргументом proxy
, а скорее с внутренней копией, сделанной из нее proxy
.