Явное совпадение аргументов шаблона - PullRequest
4 голосов
/ 23 апреля 2019

Рассмотрим эту функцию:

template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
    std::cout << container.size() << std::endl;
}

Функция foo() принимает std::vector<T, Alloc>, но она также принимает std::map<Key, T, Compare, Allocator> в C ++ 17, потому что Compare и Allocator имеют значения по умолчанию. Примечание std::map терпит неудачу в C ++ 14.

Как я могу сделать так, чтобы foo() принимал только шаблоны только с 2-мя параметрами шаблона, поэтому он не работает с std::map в C ++ 17?

Ответы [ 3 ]

3 голосов
/ 23 апреля 2019

Как я могу заставить foo принимать только шаблоны только с двумя параметрами шаблона, поэтому он не работает с std :: map в C ++ 17?

Если вы хотите избежать этого foo() принять контейнер, принимающий три или более параметров шаблона (поэтому не работает с std::map), относительно просто: вы можете следовать предложению rtpax или, учитывая особенности пользовательского типа, которые говорят, основан ли тип на принимающем контейнере двух типов

template <typename>
struct accept2 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
 { };

и признаки аналогичного типа для трехпринимающего контейнера

template <typename>
struct accept3 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
 { };

Вы можете включить SFINAE foo(), только если выведенный тип принимает два, но не принимает три типа

template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const & container)
 { }

Но у этого решения (и также у rtpax) есть проблема: как насчет контейнера, который получает до двух типов шаблонов и после одного (или более) параметра не типового шаблона со значениями по умолчанию?

Или два типа шаблона и один (или более) параметр шаблона-шаблона со значениями по умолчанию?Может быть, с разными подписями?

Вы можете добавить специализации для accept3, чтобы распознавать другие случаи, но есть бесконечные комбинации типа, нетипа и параметра шаблона-шаблона со значением по умолчанию после первых двух шаблонов.тип.Поэтому вам нужно написать бесконечную специализацию для перехвата всех случаев.

Это немного непрактично.

И я подозреваю, что (в C ++ 17) практического решения нет.

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

#include <map>
#include <vector>
#include <type_traits>

template <typename>
struct accept2 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y>
struct accept2<C<X, Y>> : std::true_type
 { };

template <typename>
struct accept3 : std::false_type
 { };

template <template <typename...> class C, typename X, typename Y, typename Z>
struct accept3<C<X, Y, Z>> : std::true_type
 { };

template <typename C>
std::enable_if_t<accept2<C>{} && !accept3<C>{}> foo (C const &)
 { }

int main()
 {
   std::vector<int> v;
   std::map<int,int> m;

   foo(v);   // compile
   //foo(m); // compilation error
 }
2 голосов
/ 23 апреля 2019

Создание шаблона перегрузки функции, которая принимает контейнер с тремя элементами. Когда вы пытаетесь использовать контейнер с двумя параметрами, он будет работать, когда вы пытаетесь использовать что-то с тремя параметрами, причем третий имеет значение по умолчанию (например, std::map), он потерпит неудачу (поскольку любой из них может его перегрузить).

#include <map>
#include <vector>
#include <iostream>

template<template<class, class, class> class C, class Key, class T, class Alloc>
void foo(C<Key, T, Alloc>& container) {
    std::cout << "three arguments" << std::endl;
}

template<template<class, class> class C, class T, class Alloc>
void foo(C<T, Alloc>& container) {
    std::cout << "two arguments" << std::endl;
}



int main() {
    std::vector<int> v;
    std::map<int,int> m;

    foo(v);
    foo(m);
}

обратите внимание, что это не работает, если кто-то вводит что-то вроде

template <class A, class B, class C> bar{};

, поскольку это будет соответствовать только параметру с тремя параметрами, поэтому оно не будет неоднозначным

1 голос
/ 23 апреля 2019

Добавьте один уровень косвенности, который использует аргумент шаблона переменной вместо двух фиксированных.Затем вы можете использовать старый добрый enable_if, чтобы отключить его, если счет не равен двум:

template<template<class...> class C, class T, class Alloc>
void foo_impl(C<T, Alloc>& container) {
    std::cout << container.size() << std::endl;
}

template<template<class...> class C, class... Args>
std::enable_if_t<sizeof...(Args) == 2> foo(C<Args...>& container) {
    foo_impl(container);
}

См. здесь , как это работает.

Примечание: очевидно* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * не обрабатывает правильно. clang (экспериментальные концепции) делает, однако.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...