Как создать шаблон функции со смесью параметров шаблона, отличных от типа, и параметров шаблона типа?

#c #templates

#c #шаблоны

Вопрос:

Минимальный воспроизводимый пример

 #include <unordered_map>
#include <string>

template<class T, bool did_work>
class Test {
    Test(T input) : field(input), success(did_work) {}
    T field;
    bool success;
};

template<typename A>
void func(std::unordered_map<std::string, Test<A, bool>> input1, A input2) {}

int main() {}
 

Вывод:

 $ g   -std=c  17 -ggdb -g3 -Wall test.cpp amp;amp; ./a.out 
test.cpp:12:51: error: type/value mismatch at argument 2 in template parameter list fortemplate<class T, bool did_work> class Test’
   12 | void func(std::unordered_map<std::string, Test<A, bool>> input1, A input2) {}
      |                                                   ^~~~
test.cpp:12:51: note:   expected a constant of type ‘bool’, got ‘bool’
test.cpp:12:55: error: template argument 2 is invalid
   12 | void func(std::unordered_map<std::string, Test<A, bool>> input1, A input2) {}
      |                                                       ^~
test.cpp:12:55: error: template argument 5 is invalid
 

Мне нужно иметь возможность передавать, unordered_map<std::string, Test<A, true>> а также unordered_map<std::string, Test<A, false>> . Есть ли способ сделать это в определении функции без изменения определения класса Test ?

Ответ №1:

With template<class T, bool did_work> did_work — параметр шаблона, отличающийся от типа. Это означает, что вместо передачи ему типа он принимает значение. Поскольку для этого требуется значение, вы делаете Test like Test<A, true> или Test<A, false> , not Test<A, bool> .

Для вашей функции вы можете просто добавить параметр шаблона, отличающийся от типа, чтобы сделать это по своему усмотрению

 template<typename A, bool did_work>
void func(std::unordered_map<std::string, Test<A, did_work>> input1, A input2) {}
 

Комментарии:

1. итак, нет ли способа обойти это, не определив два экземпляра функции, один из которых принимает Test<A, false> , а другой принимает Test<A, true> ?

2. @ajoseps Только что добавил в ответ, показывающий, как настроить шаблон для использования обоих.

Ответ №2:

Сделайте это так:

 #include <unordered_map>
#include <string>

template<class T, bool did_work>
class Test {
    Test(T input) : field(input), success(did_work) {}
    T field;
    bool success;
};

template<typename A, bool b = true>
void func(std::unordered_map<std::string, Test<A, b>> input1, A input2)
{

}

int main() {

}
 

Обновить

Я имею в виду, что вы пишете что-то вроде:

 #include <unordered_map>
#include <string>
#include <iostream>

template<class T, bool did_work>
class Test {
public:
    Test(T input) : field(input), success(did_work) {}
private:
    T field;
    bool success;
};

template<
    typename UnorderedMap
    ,typename A
> requires requires (UnorderedMap map){
    map.size();
}
void func(
    UnorderedMap input1,
    A input2)
{
    std::cout << input1.size();
}

int main() 
{
    using A = int;
    Test<A, true> t{12};
    std::unordered_map<std::string, Test<A, true>> map{};
    func(map, 12);

    //this would fail!
    //func(12, 12);
}
 

Комментарии:

1. Но в целом вам следует избегать таких строгих требований к типу. Я бы выбрал концепции / ограничения или SFINAE, если у вас есть какие-то требования.

2. не возражаете ли вы расширить понятия / ограничения или подход SFINAE? Я не слишком хорошо знаком с ними

3. Кроме того, вы не должны использовать параметры «по значению» для контейнеров — это может быть дорого…

4. можете ли вы объяснить, почему это может быть дорого?

5. Если вы переносите большие объекты по значению, вам может потребоваться скопировать много данных