Заставьте пользователей моей библиотеки также вызывать функции с примитивными значениями и именами классов

#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:

Есть две ошибки.

  1. setup_layers<Args...>(); не передает никакого интегрального аргумента шаблона setup_layers . Нет определения setup_layers , в котором не было unsigned бы параметра шаблона значения в первой позиции, поэтому этот вызов ничему не соответствует. Попробуйте, например setup_layers<index 1, Args...>()>; , вместо этого.
  2. Две перегрузки неоднозначны, когда передается ровно один параметр типа.
      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 Что вы хотите сделать со смесью индексов и названий классов?