Почему компилятор не может определить значение моего шаблона из аргумента функции?

Следующее не компилируется:

enum E {A,B,C};
template<E m> 
void foo(E m) {}

int main() {
    foo(A);
    return 0;
}

Я получаю следующие ошибки:

  • объявление 'E m': void foo (E m) {}: shadows template parm 'E m'
  • ошибка: нет соответствующей функции для вызова 'foo (E)': foo (A);
  • кандидат: template void foo (E): void foo (E m) {}
  • ошибка вывода / замены аргумента шаблона: не удалось вывести параметр шаблона 'm': foo (A);

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

Что мне нужно сделать, чтобы это сработало?


person StephenD    schedule 06.07.2015    source источник
comment
Никакого вывода не происходит, поскольку это параметр шаблона не типовой. Все, что вам нужно, это void foo(E m).   -  person 0x499602D2    schedule 06.07.2015
comment
void foo(E m) не является шаблонной функцией.   -  person nouney    schedule 06.07.2015
comment
Нет! Я хочу шаблон не по типу, а по значению.   -  person StephenD    schedule 06.07.2015


Ответы (3)


Если вам нужен аргумент времени выполнения, используйте:

void foo(E m) {}

Имеет значение m типа E. (примечание: template<E m> не требуется)

Если вам нужен аргумент времени компиляции, используйте:

template<E m> 
void foo() {}

и звоните:

foo<A>();

Или, если вы хотите, чтобы foo работал для всех типов перечислений:

template<typename E> 
void foo(E m) {}

И, вероятно, проверьте перечисления с помощью

static_assert(std::is_enum<E>::value, "E is not an enumeration");

в теле функции. (Вы также можете использовать SFINAE для удаления foo из набора перегрузки, если это необходимо, спросите, нужна ли вам помощь с этим)


Обновление: объяснение исходного кода и того, что с ним не так:

template<E m> void foo(E m) {}
//       ^^^ (1)       ^^^ (2)

(1) - это аргумент времени компиляции m типа E, (2) - это аргумент времени выполнения, также, который называется m и также имеет тип E. Поскольку у него то же имя, второй аргумент скрывает первый. Использование имени m в вашей функции предоставит доступ только ко второму аргументу, а вы не сможете получить доступ к первому. Теперь рассмотрим:

template<E m1> void foo(E m2) {}

Теперь вы можете получить доступ к аргументам под разными именами, то есть m1 и m2. Если вы вызываете функцию так:

foo<A>(B);

тогда m1 равно A, а m2 равно B. И все же оба относятся к одному и тому же фиксированному типу E. Это независимые параметры, и значение параметра времени выполнения не будет использоваться для параметра времени компиляции.

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

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

person Daniel Frey    schedule 06.07.2015
comment
Спасибо. Очевидно, мое реальное приложение несколько сложнее этого; член перечисления определяет тип возвращаемого значения функции. Я хочу, чтобы он работал только с перечислением типа E, но он должен генерировать разные реализации в зависимости от члена перечисления, используемого в качестве аргумента. На данный момент моя работа заключается в том, чтобы указать параметр шаблона, как вы предложили, но я не понимаю, зачем мне это нужно. Как аргумент функции скрывает параметр шаблона? - person StephenD; 06.07.2015
comment
Моя проблема просто в том, что я хотел бы иметь возможность вызвать эту функцию с аргументом функции и заставить компилятор сделать вывод, что то же значение должно использоваться для создания шаблона, так же, как вы можете с вычетом параметра типа шаблона. Я бы хотел избежать вызова foo ‹A› (A) и просто вызова foo (A); компилятор выводит, что мне требуется foo ‹A› (A), потому что имя значения шаблона совпадает с именем аргумента функции. Это отлично работает для параметров типа шаблона, но я не вижу документации, чтобы сказать, что это не может работать для значений шаблона. - person StephenD; 07.07.2015
comment
@StephenD Единственный вариант - тогда вызвать foo<A>() - в противном случае тип возвращаемого значения никогда не может зависеть от значения параметра (не шаблона), а только от его типа. - person Daniel Frey; 07.07.2015
comment
Спасибо. Кстати, я не новичок. Я писал код шаблона в течение многих лет и полностью понимаю разницу между параметрами времени компиляции и времени выполнения. Здесь я использую значение шаблона времени компиляции, но я не могу найти никакой документации об этой проблеме сокрытия имени со значениями шаблона. Это не проблема, например, при использовании значения для определения размера массива. Возможно, мне следует повторно опубликовать этот вопрос с более полным примером и примером размера массива в качестве сравнения. - person StephenD; 08.07.2015

Думаю, вы хотели написать:

enum E {A,B,C};
template<typename T> 
void foo(T m) {}

int main() {
    foo(A);
    return 0;
}
person ISanych    schedule 06.07.2015
comment
Нет! Я хочу использовать шаблон для члена перечисления, а не для типа. - person StephenD; 06.07.2015

Меня наконец осенило, почему это не компилируется. Хотя в моем конкретном случае аргумент функции является константой времени компиляции (член перечисления), в целом аргумент функции зависит от времени выполнения. Значение шаблона должно быть константой времени компиляции, поэтому значения аргументов функции не допускаются для вывода значений шаблона. Если механизмом обеспечения этого является сокрытие имени, то я думаю, что это случайность. Вывод состоит в том, что в то время как типы шаблонных функций могут быть выведены из типа аргумента функции, значения шаблона не выводятся.

person StephenD    schedule 08.07.2015