2193. Default constructors for standard library containers are explicit

Section: 26 [containers] Status: C++14 Submitter: Richard Smith Opened: 2012-10-04 Last modified: 2017-07-06

Priority: 1

View other active issues in [containers].

View all other issues in [containers].

View all issues with C++14 status.

Discussion:

Most (all?) of the standard library containers have explicit default constructors. Consequently:

std::set<int> s1 = { 1, 2 }; // ok
std::set<int> s2 = { 1 }; // ok
std::set<int> s3 = {}; // ill-formed, copy-list-initialization selected an explicit constructor

Note that Clang + libc++ rejects the declaration of s3 for this reason. This cannot possibly match the intent.

Suggested fix: apply this transformation throughout the standard library:

set() : set(Compare()) {}
explicit set(const Compare& comp = Compare(),
             const Allocator& = Allocator());

[ 2012-10-06: Daniel adds concrete wording. ]

[2012, Portland: Move to Open]

This may be an issue better solved by a core language tweak. Throw the issue over to EWG and see whether they believe the issue is better resolved in Core or Library.

AJM suggest we spawn a new status of 'EWG' to handle such issues - and will move this issue appropriately when the software can record such resolutions.

[2013-08-27, Joaquín M López Muñoz comments:]

For the record, I'd like to point out that the resolution proposed by the submitter, namely replacing

explicit basic_string(const Allocator& a = Allocator());

by

basic_string() : basic_string(Allocator()) {}
explicit basic_string(const Allocator& a);

(and similarly for other container and container-like classes) might introduce a potential backwards-compatibility problem related with explicit instantiation. Consider for instance

struct my_allocator
{
  my_allocator(...); // no default ctor
  ...
};

template class std::basic_string<char, std::char_traits<char>, my_allocator<char>>;

This (which I understand is currently a valid explicit instantiation of std::basic_string) will break if std::basic_string ctors are modified as proposed by this issue, since my_allocator doesn't have a default ctor.

[2013-10-06, Daniel comments:]

Issue 2303 describes the more general problem related to explicit instantiation requests in the current library and may help to solve this problem here as well.

[2014-02-13, Issaquah, Jonathan revises wording]

Previous resolution from Daniel [SUPERSEDED]:

This wording is relative to N3376.

The more general criterion for performing the suggested transformation was: Any type with an initializer-list constructor that also has an explicit default constructor.

  1. Change class template basic_string synopsis, 24.3.2 [basic.string] p5 as indicated:

    basic_string() : basic_string(Allocator()) {}
    explicit basic_string(const Allocator& a = Allocator());
    
  2. Change 24.3.2.2 [string.cons] before p1 as indicated:

    explicit basic_string(const Allocator& a = Allocator());
    
  3. Change class template deque synopsis, 26.3.8.1 [deque.overview] p2 as indicated:

    deque() : deque(Allocator()) {}
    explicit deque(const Allocator& = Allocator());
    
  4. Change 26.3.8.2 [deque.cons] before p1 as indicated:

    explicit deque(const Allocator& = Allocator());
    
  5. Change class template forward_list synopsis, 26.3.9.1 [forwardlist.overview] p3 as indicated:

    forward_list() : forward_list(Allocator()) {}
    explicit forward_list(const Allocator& = Allocator());
    
  6. Change 26.3.9.2 [forwardlist.cons] before p1 as indicated:

    explicit forward_list(const Allocator& = Allocator());
    
  7. Change class template list synopsis, 26.3.10.1 [list.overview] p2 as indicated:

    list() : list(Allocator()) {}
    explicit list(const Allocator& = Allocator());
    
  8. Change 26.3.10.2 [list.cons] before p1 as indicated:

    explicit list(const Allocator& = Allocator());
    
  9. Change class template vector synopsis, 26.3.11.1 [vector.overview] p2 as indicated:

    vector() : vector(Allocator()) {}
    explicit vector(const Allocator& = Allocator());
    
  10. Change 26.3.11.2 [vector.cons] before p1 as indicated:

    explicit vector(const Allocator& = Allocator());
    
  11. Change class template specialization vector<bool> synopsis, 26.3.12 [vector.bool] p1 as indicated:

    vector() : vector(Allocator()) {}
    explicit vector(const Allocator& = Allocator());
    
  12. Change class template map synopsis, 26.4.4.1 [map.overview] p2 as indicated:

    map() : map(Compare()) {}
    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  13. Change 26.4.4.2 [map.cons] before p1 as indicated:

    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  14. Change class template multimap synopsis, 26.4.5.1 [multimap.overview] p2 as indicated:

    multimap() : multimap(Compare()) {}
    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  15. Change 26.4.5.2 [multimap.cons] before p1 as indicated:

    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  16. Change class template set synopsis, 26.4.6.1 [set.overview] p2 as indicated:

    set() : set(Compare()) {}
    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  17. Change 26.4.6.2 [set.cons] before p1 as indicated:

    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  18. Change class template multiset synopsis, 26.4.7.1 [multiset.overview] p2 as indicated:

    multiset() : multiset(Compare()) {}
    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  19. Change 26.4.7.2 [multiset.cons] before p1 as indicated:

    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  20. Change class template unordered_map synopsis, 26.5.4.1 [unord.map.overview] p3 as indicated:

    unordered_map() : unordered_map(see below) {}
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  21. Change 26.5.4.2 [unord.map.cnstr] before p1 as indicated:

    unordered_map() : unordered_map(see below) {}
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  22. Change class template unordered_multimap synopsis, 26.5.5.1 [unord.multimap.overview] p3 as indicated:

    unordered_multimap() : unordered_multimap(see below) {}
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  23. Change 26.5.5.2 [unord.multimap.cnstr] before p1 as indicated:

    unordered_multimap() : unordered_multimap(see below) {}
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  24. Change class template unordered_set synopsis, 26.5.6.1 [unord.set.overview] p3 as indicated:

    unordered_set() : unordered_set(see below) {}
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  25. Change 26.5.6.2 [unord.set.cnstr] before p1 as indicated:

    unordered_set() : unordered_set(see below) {}
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  26. Change class template unordered_multiset synopsis, 26.5.7.1 [unord.multiset.overview] p3 as indicated:

    unordered_multiset() : unordered_multiset(see below) {}
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  27. Change 26.5.7.2 [unord.multiset.cnstr] before p1 as indicated:

    unordered_multiset() : unordered_multiset(see below) {}
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    

[Issaquah 2014-02-11: Move to Immediate after final review]

Proposed resolution:

This wording is relative to N3376.

The more general criterion for performing the suggested transformation was: Any type with an initializer-list constructor that also has an explicit default constructor.

  1. Change class template basic_string synopsis, 24.3.2 [basic.string] p5 as indicated:

    basic_string() : basic_string(Allocator()) { }
    explicit basic_string(const Allocator& a = Allocator());
    
  2. Change 24.3.2.2 [string.cons] before p1 as indicated:

    explicit basic_string(const Allocator& a = Allocator());
    
  3. Change class template deque synopsis, 26.3.8.1 [deque.overview] p2 as indicated:

    deque() : deque(Allocator()) { }
    explicit deque(const Allocator& = Allocator());
    
  4. Change 26.3.8.2 [deque.cons] before p1 as indicated:

    explicit deque(const Allocator& = Allocator());
    
  5. Change class template forward_list synopsis, 26.3.9.1 [forwardlist.overview] p3 as indicated:

    forward_list() : forward_list(Allocator()) { }
    explicit forward_list(const Allocator& = Allocator());
    
  6. Change 26.3.9.2 [forwardlist.cons] before p1 as indicated:

    explicit forward_list(const Allocator& = Allocator());
    
  7. Change class template list synopsis, 26.3.10.1 [list.overview] p2 as indicated:

    list() : list(Allocator()) { }
    explicit list(const Allocator& = Allocator());
    
  8. Change 26.3.10.2 [list.cons] before p1 as indicated:

    explicit list(const Allocator& = Allocator());
    
  9. Change class template vector synopsis, 26.3.11.1 [vector.overview] p2 as indicated:

    vector() : vector(Allocator()) { }
    explicit vector(const Allocator& = Allocator());
    
  10. Change 26.3.11.2 [vector.cons] before p1 as indicated:

    explicit vector(const Allocator& = Allocator());
    
  11. Change class template specialization vector<bool> synopsis, 26.3.12 [vector.bool] p1 as indicated:

    vector() : vector(Allocator()) { }
    explicit vector(const Allocator& = Allocator());
    
  12. Change class template map synopsis, 26.4.4.1 [map.overview] p2 as indicated:

    map() : map(Compare()) { }
    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  13. Change 26.4.4.2 [map.cons] before p1 as indicated:

    explicit map(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  14. Change class template multimap synopsis, 26.4.5.1 [multimap.overview] p2 as indicated:

    multimap() : multimap(Compare()) { }
    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  15. Change 26.4.5.2 [multimap.cons] before p1 as indicated:

    explicit multimap(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  16. Change class template set synopsis, 26.4.6.1 [set.overview] p2 as indicated:

    set() : set(Compare()) { }
    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  17. Change 26.4.6.2 [set.cons] before p1 as indicated:

    explicit set(const Compare& comp = Compare(),
                 const Allocator& = Allocator());
    
  18. Change class template multiset synopsis, 26.4.7.1 [multiset.overview] p2 as indicated:

    multiset() : multiset(Compare()) { }
    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  19. Change 26.4.7.2 [multiset.cons] before p1 as indicated:

    explicit multiset(const Compare& comp = Compare(),
                      const Allocator& = Allocator());
    
  20. Change class template unordered_map synopsis, 26.5.4.1 [unord.map.overview] p3 as indicated:

    unordered_map();
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  21. Change 26.5.4.2 [unord.map.cnstr] before p1 as indicated:

    unordered_map() : unordered_map(size_type(see below)) { }
    explicit unordered_map(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
    -1- Effects: Constructs an empty unordered_map using the specified hash function, key equality func-
    tion, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  22. Change class template unordered_multimap synopsis, 26.5.5.1 [unord.multimap.overview] p3 as indicated:

    unordered_multimap();
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  23. Change 26.5.5.2 [unord.multimap.cnstr] before p1 as indicated:

    unordered_multimap() : unordered_multimap(size_type(see below)) { }
    explicit unordered_multimap(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
    -1- Effects: Constructs an empty unordered_multimap using the specified hash function, key equality
    function, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  24. Change class template unordered_set synopsis, 26.5.6.1 [unord.set.overview] p3 as indicated:

    unordered_set();
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
  25. Change 26.5.6.2 [unord.set.cnstr] before p1 as indicated:

    unordered_set() : unordered_set(size_type(see below)) { }
    explicit unordered_set(size_type n = see below,
                           const hasher& hf = hasher(),
                           const key_equal& eql = key_equal(),
                           const allocator_type& a = allocator_type());
    
    -1- Effects: Constructs an empty unordered_set using the specified hash function, key equality func-
    tion, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.
  26. Change class template unordered_multiset synopsis, 26.5.7.1 [unord.multiset.overview] p3 as indicated:

    unordered_multiset();
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
  27. Change 26.5.7.2 [unord.multiset.cnstr] before p1 as indicated:

    unordered_multiset() : unordered_multiset(size_type(see below)) { }
    explicit unordered_multiset(size_type n = see below,
                                const hasher& hf = hasher(),
                                const key_equal& eql = key_equal(),
                                const allocator_type& a = allocator_type());
    
    -1- Effects: Constructs an empty unordered_multiset using the specified hash function, key equality
    function, and allocator, and using at least n buckets. If n is not provided, For the default constructor
    the number of buckets is implementation-defined. max_load_factor() returns 1.0.