C ++ SFINAE для массива / вектора - PullRequest
0 голосов
/ 02 мая 2020

Я хочу сделать перегруженный оператор <<, который используется только контейнерами (массив / вектор). </p>

У меня есть следующий шаблон:

namespace reachability {
    template <typename Container>
    ostream& operator<<(ostream& out, const is_container<Container>(&container)) {
        string result = "{";

        for (values_t elem : container) {
            result += std::string(elem) + ",";
        }

        std::operator<<(out, result.substr(0, result.length() - 1) + "}");
        return out;
    }

    struct values_t {
        string type;
        operator std::string() const { return type; }
    };


    template<typename T, typename _ = void>
    struct is_container : std::false_type {};

    template<typename... Ts>
    struct is_container_helper {};

    template<typename T>
    struct is_container<
        T,
        std::conditional_t<
        false,
        is_container_helper<
        typename T::value_type,
        typename T::size_type,
        typename T::allocator_type,
        typename T::iterator,
        typename T::const_iterator,
        decltype(std::declval<T>().size()),
        decltype(std::declval<T>().begin()),
        decltype(std::declval<T>().end()),
        decltype(std::declval<T>().cbegin()),
        decltype(std::declval<T>().cend())
        >,
        void
        >
    > : public std::true_type{};

}

И он используется в:

reachability::values_t vals[5];

int main(int argc, char** argv)
{
    vals[0].type = "zoo";
    vals[1].type = "foo";
    vals[2].type = "loo";
    vals[3].type = "koo";
    vals[4].type = "moo";
    /*elements_t elems = { space::half };*/
    reachability::operator<<(cout, vals);
    return 0;
}

Однако я получаю сообщение об ошибке, что ни один экземпляр перегруженного оператора не соответствует списку аргументов.

У меня та же проблема, если я изменяю шаблон оператора на:

ostream& operator<<(ostream& out, const Container (&container)[]) 

Мне удалось заставить его работать, выполнив:

ostream& operator<<(ostream& out, const Container (&container)) 

Ответы [ 2 ]

2 голосов
/ 02 мая 2020

Если вы хотите получить ошибку во время компиляции при объявлении вашей функции, а не при создании экземпляра тела этого шаблона функции, вы создаете SFINAE, добавляя другой параметр шаблона, синтаксис которого проверен, и если он не делает ничего В смысле, компилятор скажет вам об этом, сказав нет соответствующей функции для вызова ... :

template <
  typename Container, 
  std::enable_if_t< is_container<Container>::value | 
                    std::is_array_v<Container> >* = nullptr>
ostream& operator<<(ostream& out, const Container&container ) {

, как вы видите, добавлен второй параметр шаблона, он будет void* = nullptr (который является допустимым синтаксисом), только если Container - это некоторый тип класса, который поддерживает begin / end / size et c или это встроенный тип массива (T []). В противном случае компилятор жалуется, что не может получить type для объявления type* = nullptr.


std::vector<reachability::values_t> v;
std::list<reachability::values_t> l;
std::pair<int,int> p;
reachability::operator<<(cout, vals); // OK
reachability::operator<<(cout, v); // OK
reachability::operator<<(cout, l); // OK
reachability::operator<<(cout, p); // Wrong, compile-time error, no match ...

Демо

1 голос
/ 02 мая 2020

Если вы хотите использовать определенный вами is_container, используйте

template<
typename Container, 
std::enable_if_t<is_container<Container>::value, int>= 0>
ostream& operator<<(ostream& out, const Container &container) {}

enable_if_t , чтобы скрыть функцию от разрешения перегрузки на основе логического значения is_container<Container>::value. Обратите внимание, что ваше определение is_container будет принимать коллекцию

std::vector<reachability::values_t> vals(5);

, но не массив

reachability::values_t vals[5];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...