Short answer
For s2, brace syntax is used, and {a.begin(), a.end()} is considered to be an initializer_list of std::array<int>::iterators. Therefore, s2 is a set of iterators.
For s3, parentheses syntax is used, and the iterator constructor is selected. s3 is a set of ints, and is initialized from the range [a.begin(), a.end()).
Long answer
Per [set.overview], we have two deduction guides relevant here:
template<class InputIterator,
class Compare = less<typename iterator_traits<InputIterator>::value_type>,
class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
set(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
and
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>;
Per [over.match.class.deduct]/1:
A set of functions and function templates is formed comprising:
In this case, the synthesized functions and function templates for the aforementioned deduction guides are, respectively,
template<class InputIterator,
class Compare = less<typename iterator_traits<InputIterator>::value_type>,
class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
auto __func1(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
and
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
auto __func2(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>;
(I used double underscores to signify that these names are synthesized, and are not otherwise accessible.)
Per [over.match.class.deduct]/2:
Initialization and overload resolution are performed as described in
[dcl.init] and [over.match.ctor], [over.match.copy], or
[over.match.list] (as appropriate for the type of initialization
performed) for an object of a hypothetical class type, where the
selected functions and function templates are considered to be the
constructors of that class type for the purpose of forming an overload
set, and the initializer is provided by the context in which class
template argument deduction was performed. Each such notional
constructor is considered to be explicit if the function or function
template was generated from a constructor or deduction-guide that
was declared explicit. All such notional constructors are considered
to be public members of the hypothetical class type.
The hypothetical class type looks like:
class __hypothetical {
public:
// ...
// #1
template<class InputIterator,
class Compare = less<typename iterator_traits<InputIterator>::value_type>,
class Allocator = allocator<typename iterator_traits<InputIterator>::value_type>>
__hypothetical(InputIterator, InputIterator,
Compare = Compare(), Allocator = Allocator())
-> set<typename iterator_traits<InputIterator>::value_type, Compare, Allocator>;
// #2
template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>>
__hypothetical(initializer_list<Key>, Compare = Compare(), Allocator = Allocator())
-> set<Key, Compare, Allocator>;
// ...
};
For the declaration of s2,
std::set s2 {a.begin(), a.end()};
The overload resolution is performed as if in
__hypothetical __hyp{a.begin(), a.end()}; // braces
Thus, [over.match.list] comes in.
[...] overload resolution selects the constructor in two phases:
Constructor #2 is an initializer-list constructor. Function template argument deduction gives
Key = std::array<int>::iterator
So the deduced type of s2 is
std::set<std::array<int>::iterator>
The declaration of s2 is equivalent to
std::set<std::array<int>::iterator> s2 {a.begin(), a.end()};
Therefore, s2 is a set of iterators that consists of two elements: a.begin() and a.end(). In your case, std::array<int>::iterator is probably int*, and a.begin() and a.end() happen to be serialized as 0x7ffecf9d12e0 and 0x7ffecf9d12f0, respectively.
For s3, overload resolution is performed as if in
__hypothetical __hyp(a.begin(), a.end()); // parentheses
That's direct-initialization, and is under the scope of [pver.match.ctor]. The initializer_list constructor is irrelevant, and the Constructor #1 is selected instead. Function template argument deduction gives
InputIterator = std::array<int>::iterator
So the deduced type of s3 is
set<iterator_traits<std::array<int>::iterator>::value_type>
Which is set<int>. Therefore, the declaration of s3 is equivalent to
std::set<int> s3 (a.begin(), a.end());
s3 is a set of ints that is initialized from the range [a.begin(), a.end()) — four elements 1, 2, 3, 4, which explains the output.