Вариадические шаблоны с перегрузкой параметра '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.