Приоритеты c++-шаблонов

81
06 марта 2022, 20:20

Есть вот такой код:

#include <tuple>
#include <vector>
using namespace std;
// (1)
template <typename L, typename R>
struct combine {
    using type = tuple<L, R>;
};
// (2)
template <typename T>
struct combine<T, T> {
    using type = vector<T>;
};
// (3)
template <typename ... Ts, typename T>
struct combine<tuple<Ts...>, T> {
    using type = tuple<Ts..., T>;
};
// (4)
template <typename ... Ts, typename ... Us>
struct combine<tuple<Ts...>, tuple<Us...>> {
    using type = tuple<Ts..., Us...>;
};
int main() {
    using result = typename combine<tuple<int, char>, tuple<int, char>>::type;
    return 0;
};

Как ни странно, компилятор начал ругаться на неопределённость:

main.cpp:26:72: error: ambiguous template instantiation for 'struct combine<std::tuple<int, char>, std::tuple<int, char> >'
   26 |     using result = typename combine<tuple<int, char>, tuple<int, char>>::type;
      |                                                                        ^~
main.cpp:11:8: note: candidates are: 'template<class T> struct combine<T, T> [with T = std::tuple<int, char>]'
   11 | struct combine<T, T> {
      |        ^~~~~~~~~~~~~
main.cpp:16:8: note:                 'template<class ... Ts, class T> struct combine<std::tuple<_Tps ...>, T> [with Ts = {int, char}; T = std::tuple<int, char>]'
   16 | struct combine<tuple<Ts...>, T> {

Собственно вопрос в том, как помочь компилятору решить неоднозначность. Хотелось бы, чтобы правило (2) было в приоритете. Единственное, что пришло в голову - добавить в combine дополнительный аргумент Enable = void для SFINAE и в правилах (3) и (4) прописать enable_if_t<!is_same_v<tuple<Ts...>, T>> и enable_if_t<!is_same_v<tuple<Ts...>, tuple<Us...>>> соответственно. Но по-моему это больше костыль, чем решение.

Answer 1

Нужно добавить такую специализацию:

template <typename ...P>
struct combine<std::tuple<P...>, std::tuple<P...>>
{
    using type = std::vector<std::tuple<P...>>;
};

Но вообще, не нравится мне этот шаблон. Через пару месяцев вы сами-то вспомните, что combine с двумя одинаковыми tuple дает один результат, а с разными - другой?

Answer 2

Сначала нужно определить общий шаблон, а потом частные случаи(частичные специализации). И нужно помнить, что если шаблон класса имеет пакет параметров шаблона, он должен появиться в конце списка параметров шаблона.

// (1) с неизвестным количеством параметров (больше оодного)
template <typename T, typename ... Ts>
struct combine {
    using type = tuple<Ts..., T>;
};
// (2) когда ровно два параметра
template <typename L, typename R>
struct combine<L, R> {
    using type = tuple<L, R>;
};
// (3) с одним параметром
template <typename T>
struct combine<T, T> {
    using type = vector<T>;
};
READ ALSO
Включать header файлы в .cpp, а не .h

Включать header файлы в .cpp, а не .h

Какое преимущество в билде от того, что header файлы будут включены вcpp, а не

75
Передать по jqery в laravel

Передать по jqery в laravel

подскажите пожалуйста Есть views и контроллер Как передать переменную в контроллер ,выполнить запрос и вернуть его в views К примеру по клике...

72
Как вывести в div результат ajax?

Как вывести в div результат ajax?

Получаю ответ в json от ajax запроса и пытаюсь вывести в <div class="writeinfo"></div>

100
Переменная становится undefined внутри else

Переменная становится undefined внутри else

Всем приветВот часть моего кода в jquery

108