руководство по выводу std :: set не работает должным образом - PullRequest
2 голосов
/ 26 мая 2019

Разве два набора не должны быть идентичными?

#include <array>
#include <set>
#include <iostream>

template <class Iter>
std::set(Iter, Iter) -> std::set<typename std::iterator_traits<Iter>::value_type>;

int main() {

    std::array a {1,2,3,4};
    std::set<int> si {a.begin(), a.end()}; 
    std::set       s {a.begin(), a.end()};     

    for(auto& i: si) { std::cout << i << "\n"; }
    for(auto& i: s ) { std::cout << i << "\n"; }

}

вместо этого это дает:

1
2
3
4
0x7ffdf5bc9050
0x7ffdf5bc9060

даже пробовал с дополнительным распределителем: - (

Ответы [ 2 ]

1 голос
/ 26 мая 2019

Здесь есть две путаницы.

<ч />

Во-первых, руководства по удержанию должны находиться в области действия шаблона класса, которым они руководствуются, поэтому это руководство никогда не будет рассматриваться:

template <class Iter>
std::set(Iter, Iter) -> std::set<typename std::iterator_traits<Iter>::value_type>;

Это должно выглядеть так:

namespace std {
    template <class Iter>
    set(Iter, Iter) -> set<typename iterator_traits<Iter>::value_type>;
}

Но вы не можете добавлять вещи в namespace std, так что не делайте этого. И кроме того, это руководство по выводу уже существует для set:

template<class InputIt, 
         class Comp = std::less<typename std::iterator_traits<InputIt>::value_type>,
         class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
set(InputIt, InputIt, Comp = Comp(), Alloc = Alloc())
  -> set<typename std::iterator_traits<InputIt>::value_type, Comp, Alloc>;

Так что нет никакой причины добавлять вашу версию.

<ч />

Причина , по которой руководство по выводам не используется, и не будет, если вы поместите его в правильное пространство имен, является обычным главным предупреждением для инициализации списка:

Если есть соответствующий initializer_list<T> конструктор, настоятельно рекомендуется , чем что-либо еще.

Конкретное правило языка состоит в том, что мы сначала делаем разрешение перегрузки специально для этих конструкторов, а затем делаем остальное. И одно из других руководств по выводам в нашем списке:

template<class Key, class Comp = std::less<Key>, class Alloc = std::allocator<Key>>
set(std::initializer_list<Key>, Comp = Comp(), Alloc = Alloc())
  -> set<Key, Comp, Alloc>;

Обратите внимание:

std::set s{a.begin(), a.end()};     

точно такой же вид конструкции как:

std::set u{1, 2};

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

Если вы хотите использовать другое руководство по выводам с другим конструктором, вы должны использовать скобки:

std::set s(a.begin(), a.end());     

И удалите руководство по удержанию!

0 голосов
/ 26 мая 2019

std::set предоставляет конструктор, который принимает std::initializer_list и способен делать вывод типа T для std::set<T>.В этом случае использование синтаксиса std::set x{a.begin(), a.end() }; будет использовать конструктор списка инициализатора и выводит T из этого.Никакого пользовательского руководства по выводам не происходит!

Если вы создаете свой объект с помощью std::set x(a.begin(), a.end());, конструктор списка инициализатора будет не использоваться.Теперь, когда никакие другие конструкторы не совпадают, действуют руководства по выводу!

Пример того, каков эффект, если в вашем коде есть конструкторы списка инициализатора:

template < typename U >
struct X
{
    // Directly makes the type deducable, because U can be deduced from the parameter of the constructor
    X( std::initializer_list<U> )
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    template < typename ... T>
        X( T... )
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};



struct ONE {};

template < typename Iter >
X( const Iter&, const Iter& ) -> X<ONE>;

template < typename U >
struct Y
{
    // type U can NOT deduced from the parameter!
    template < typename T>
    Y( std::initializer_list<T> )
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }

    template < typename ... T>
        Y( T... )
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};



struct TWO {};

template < typename Iter >
Y( const Iter&, const Iter& ) -> Y<TWO>;

int main()
{
    std::array a {1,2,3,4};
    X x1{a.begin(), a.end()};
    X x2(a.begin(), a.end());

    std::cout << "###" << std::endl;

    Y y1{a.begin(), a.end()};
    Y y2(a.begin(), a.end());
}

Эти конструкторы списка инициализатора имеют преимуществоРуководство по выводу также было новым для меня и отвечало здесь: Почему использование единого синтаксиса инициализатора приводит к поведению, отличному от «старого» стиля ()?

Важной новой вещью для меня было:

[over.match.list]

[...] разрешение перегрузки выбирает конструктор в два этапа:

  • Первоначально функции-кандидаты являются конструкторами списка инициализаторов ([dcl.init.list]) класса T, а список аргументов состоит из списка инициализаторов как единственного аргумента..

  • [...]

Подробнее см. Связанный вопрос!

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