#c #templates #variadic-templates
Вопрос:
У меня есть нетипичное требование. Я пишу функцию библиотеки настроек, которую следует вызывать только с постоянными значениями и именами классов. Я сделал что-то вроде этого:
template <unsigned index, class layerClass, typename...Args>
void setup_layers() {
//add layer
//recursively call yourself
setup_layers<Args...>();
}
template <unsigned index, class layerClass>
void setup_layers() {
//add layer
}
Когда я пытаюсь использовать свою функцию:
struct MyLayer {};
int main (int argc, char **argv)
{
constexpr unsigned LAYER_NUM = 0;
setup_layers<LAYER_NUM, MyLayer>();
return 0;
}
компилятор сообщает об ошибке call of overloaded ‘setup_layers<LAYER_NUM, MyLayer>()’ is ambiguous
Я не уверен, как я могу выполнить свое требование другим способом :(. Если бы я мог передавать имена классов в качестве параметров своей функции с помощью обычных аргументов, это было бы прекрасно, но в C такой функции нет…
РЕДАКТИРОВАТЬ ХОРОШО, похоже, мое «решение» никуда не годится и просто не работает. Поскольку я не хочу удалять вопрос с ответами, тогда, возможно, мне следует задать свой вопрос по-другому:
Я хочу, чтобы пользователи моей библиотеки задали список слоев с индексами (где числа в этих индексах не обязательно должны иметь последовательные числа). Причина, по которой я хотел сделать это с помощью шаблонов, заключается в том, что шаблоны могут допускать только постоянные значения в качестве параметров. Проще говоря: я хочу запретить своим пользователям использовать переменные в качестве параметров для индексов.
Комментарии:
1. @Питер Закончил, но все равно я получаю ту же ошибку 🙁
2. Вы не передаете какое-либо интегральное значение
setup_layers<Args...>()
. Если шаблон нуждается в нем, вы должны его передать.3. @Питер, Нет, это не так (говорит
std::array<int, 5>
).4. @TedLyngmo Я использую G 9, и я должен признать, что отчеты компилятора об ошибках на самом деле «не соответствуют функции для вызова setup_layers», но в качестве причины этого он дает мне примечание с объяснением, как я дал
5. @TedLyngmo ХОРОШО, я попытался изолировать ошибку и сделал тестовую программу, и да, теперь отчеты об ошибках компилятора отличаются. Mea culpa… Но суть моего вопроса все равно остается прежней: моя программа не компилируется :(. Я использую стандарт C 20.
Ответ №1:
Вот один из вариантов устранения двусмысленности: вы можете переименовать функцию, выполняющую фактическую реализацию установки. Я назвал это setup_impl
:
template <unsigned index, class layerClass>
void setup_impl() {
//add layer
std::cout << index << 'n';
}
template <unsigned index, class layerClass, class... Args>
void setup_layers() {
setup_impl<index, layerClass>();
if constexpr (sizeof...(Args) > 0) {
setup_layers<index 1, Args...>();
}
}
Если index
предполагается, что каждый раз, когда используется класс слоя, он должен быть одинаковым, вы можете сделать index
свойство классов слоев свойством.
Пример:
template <class layerClass>
void setup_impl() {
std::cout << layerClass::index << 'n';
}
template <class... Args>
void setup_layers() {
(setup_impl<Args>(), ...); // fold expression
}
struct MyLayer1 { static constexpr unsigned index = 11; };
struct MyLayer2 { static constexpr unsigned index = 22; };
struct MyLayer3 { static constexpr unsigned index = 33; };
Комментарии:
1. После переосмысления я решил, что ваше решение может быть единственным работоспособным. Спасибо вам за ваше терпение (его было очень много). 🙂
Ответ №2:
Есть две ошибки.
setup_layers<Args...>();
не передает никакого интегрального аргумента шаблонаsetup_layers
. Нет определенияsetup_layers
, в котором не былоunsigned
бы параметра шаблона значения в первой позиции, поэтому этот вызов ничему не соответствует. Попробуйте, напримерsetup_layers<index 1, Args...>()>;
, вместо этого.- Две перегрузки неоднозначны, когда передается ровно один параметр типа.
template <unsigned index, class layerClass, typename...Args> ... template <unsigned index, class layerClass /*, nothing */> ...
Пустой
typename ... Args
пакет является законным, и нет причин предпочитать пустой пакет параметров пустому, или наоборот. Попробуйте удалитьclass layerClass
вторую перегрузку (и пусть она ничего не делает).template <unsigned index> void setup_layers() {}
должно подойти.
Ответ №3:
Ты можешь сделать это вот так:
#include <iostream>
#include <typeinfo>
template <int index, class layerClass>
void setup_layers(int layer) {
//add layer
std::cout << "Adding layer " << layer << ": " << typeid(layerClass).name() << std::endl;
}
template <class layerClass, typename...Args>
void setup_layers(int layer) {
//add layer
//recursively call yourself
if constexpr (sizeof...(Args) > 0) {
setup_layers<0, layerClass>( layer);
setup_layers<Args...>(layer);
} else {
setup_layers<0, layerClass>(layer);
}
}
struct MyLayer {};
struct OtherLayer {};
int main() {
setup_layers<MyLayer, OtherLayer, MyLayer, OtherLayer>(0);
std::cout << "End" << std::endl;
}
Выход:
Adding layer 1: 7MyLayer
Adding layer 2: 10OtherLayer
Adding layer 3: 7MyLayer
Adding layer 3: 10OtherLayer
End
Комментарии:
1. Один вопрос:
typename...Args
может ли расширяться только до имен классов? Потому что я хочу, чтобы мой список содержал смешанный список аргументов как с индексами, так и с именами классов, так как я не всегда хочу инициализировать свои слои последовательными числами. Если это невозможно, то мне действительно следует пересмотреть свое «решение» и попробовать что-то другое (а также удалить этот пост, чтобы больше не стыдиться…) 🙁2. Нет. Вы не можете смешивать индексы и имена классов
Args
. Вы можете передать индексы в функцию в качестве переменного параметра… но это все усложняет.3. @Felix.leg Что вы хотите сделать со смесью индексов и названий классов?