1194. Unintended queue constructor

Section: 23.6 [container.adaptors] Status: C++11 Submitter: Howard Hinnant Opened: 2009-08-20 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [container.adaptors].

View all issues with C++11 status.

Discussion:

23.6.3.1 [queue.defn] has the following queue constructor:

template <class Alloc> explicit queue(const Alloc&);

This will be implemented like so:

template <class Alloc> explicit queue(const Alloc& a) : c(a) {}

The issue is that Alloc can be anything that a container will construct from, for example an int. Is this intended to compile?

queue<int> q(5);

Before the addition of this constructor, queue<int>(5) would not compile. I ask, not because this crashes, but because it is new and appears to be unintended. We do not want to be in a position of accidently introducing this "feature" in C++0X and later attempting to remove it.

I've picked on queue. priority_queue and stack have the same issue. Is it useful to create a priority_queue of 5 identical elements?

[ Daniel, Howard and Pablo collaborated on the proposed wording. ]

[ 2009-10 Santa Cruz: ]

Move to Ready.

Proposed resolution:

[ This resolution includes a semi-editorial clean up, giving definitions to members which in some cases weren't defined since C++98. This resolution also offers editorially different wording for 976, and it also provides wording for 1196. ]

Change [ontainer.adaptor], p1:

The container adaptors each take a Container template parameter, and each constructor takes a Container reference argument. This container is copied into the Container member of each adaptor. If the container takes an allocator, then a compatible allocator may be passed in to the adaptor's constructor. Otherwise, normal copy or move construction is used for the container argument. [Note: it is not necessary for an implementation to distinguish between the one-argument constructor that takes a Container and the one- argument constructor that takes an allocator_type. Both forms use their argument to construct an instance of the container. — end note]

Change [ueue.def], p1:

template <class T, class Container = deque<T> >
class queue {
public:
  typedef typename Container::value_type      value_type;
  typedef typename Container::reference       reference;
  typedef typename Container::const_reference const_reference;
  typedef typename Container::size_type       size_type;
  typedef Container                           container_type;
protected:
  Container c;

public:
  explicit queue(const Container&);
  explicit queue(Container&& = Container());
  queue(queue&& q); : c(std::move(q.c)) {}
  template <class Alloc> explicit queue(const Alloc&);
  template <class Alloc> queue(const Container&, const Alloc&);
  template <class Alloc> queue(Container&&, const Alloc&);
  template <class Alloc> queue(queue&&, const Alloc&);
  queue& operator=(queue&& q); { c = std::move(q.c); return *this; }

  bool empty() const          { return c.empty(); }
  ...
};

Add a new section after 23.6.3.1 [queue.defn], [queue.cons]:

queue constructors [queue.cons]

explicit queue(const Container& cont);

Effects: Initializes c with cont.

explicit queue(Container&& cont = Container());

Effects: Initializes c with std::move(cont).

queue(queue&& q)

Effects: Initializes c with std::move(q.c).

For each of the following constructors, if uses_allocator<container_type, Alloc>::value is false, then the constructor shall not participate in overload resolution.

template <class Alloc> 
  explicit queue(const Alloc& a);

Effects: Initializes c with a.

template <class Alloc> 
  queue(const container_type& cont, const Alloc& a);

Effects: Initializes c with cont as the first argument and a as the second argument.

template <class Alloc> 
  queue(container_type&& cont, const Alloc& a);

Effects: Initializes c with std::move(cont) as the first argument and a as the second argument.

template <class Alloc> 
  queue(queue&& q, const Alloc& a);

Effects: Initializes c with std::move(q.c) as the first argument and a as the second argument.

queue& operator=(queue&& q);

Effects: Assigns c with std::move(q.c).

Returns: *this.

Add to 23.6.4.2 [priqueue.cons]:

priority_queue(priority_queue&& q);

Effects: Initializes c with std::move(q.c) and initializes comp with std::move(q.comp).

For each of the following constructors, if uses_allocator<container_type, Alloc>::value is false, then the constructor shall not participate in overload resolution.

template <class Alloc>
  explicit priority_queue(const Alloc& a);

Effects: Initializes c with a and value-initializes comp.

template <class Alloc>
  priority_queue(const Compare& compare, const Alloc& a);

Effects: Initializes c with a and initializes comp with compare.

template <class Alloc>
  priority_queue(const Compare& compare, const Container& cont, const Alloc& a);

Effects: Initializes c with cont as the first argument and a as the second argument, and initializes comp with compare.

template <class Alloc>
  priority_queue(const Compare& compare, Container&& cont, const Alloc& a);

Effects: Initializes c with std::move(cont) as the first argument and a as the second argument, and initializes comp with compare.

template <class Alloc>
  priority_queue(priority_queue&& q, const Alloc& a);

Effects: Initializes c with std::move(q.c) as the first argument and a as the second argument, and initializes comp with std::move(q.comp).

priority_queue& operator=(priority_queue&& q);

Effects: Assigns c with std::move(q.c) and assigns comp with std::move(q.comp).

Returns: *this.

Change 23.6.6.2 [stack.defn]:

template <class T, class Container = deque<T> >
class stack {
public:
  typedef typename Container::value_type      value_type;
  typedef typename Container::reference       reference;
  typedef typename Container::const_reference const_reference;
  typedef typename Container::size_type       size_type;
  typedef Container                           container_type;
protected:
  Container c;

public:
  explicit stack(const Container&);
  explicit stack(Container&& = Container());
  stack(stack&& s);
  template <class Alloc> explicit stack(const Alloc&);
  template <class Alloc> stack(const Container&, const Alloc&);
  template <class Alloc> stack(Container&&, const Alloc&);
  template <class Alloc> stack(stack&&, const Alloc&);
  stack& operator=(stack&& s);

  bool empty() const          { return c.empty(); }
  ...
};

Add a new section after 23.6.6.2 [stack.defn], [stack.cons]:

stack constructors [stack.cons]

stack(stack&& s);

Effects: Initializes c with std::move(s.c).

For each of the following constructors, if uses_allocator<container_type, Alloc>::value is false, then the constructor shall not participate in overload resolution.

template <class Alloc> 
  explicit stack(const Alloc& a);

Effects: Initializes c with a.

template <class Alloc> 
  stack(const container_type& cont, const Alloc& a);

Effects: Initializes c with cont as the first argument and a as the second argument.

template <class Alloc> 
  stack(container_type&& cont, const Alloc& a);

Effects: Initializes c with std::move(cont) as the first argument and a as the second argument.

template <class Alloc> 
  stack(stack&& s, const Alloc& a);

Effects: Initializes c with std::move(s.c) as the first argument and a as the second argument.

stack& operator=(stack&& s);

Effects: Assigns c with std::move(s.c).

Returns: *this.