1

I am wondering if this an stdlib bug, an oversight, my own error or intended by the standards committee.

If a type T's constructor (any of its constructors) is non-public, an unrelated container cannot emplace an element using that constructor, clearly. However, my reasoning was that if I befriend said container, various emplace's should be allowed, but due to the way the standard library containers are generally implemented over myriad of sub- and helper-classes this doesn't seem to work.

Consider this:

#include <optional>

class T { 
  friend std::optional<T>;
  T(int) {}
};  

int main() {
  std::optional<T> opt;
  opt.emplace(7);
}

If this is compiled with g++ 10.2, it complains that no suitable function std::optional<T>::emplace(int) exists. Notably it does not complain that T::T(int) is private within this context as I would have expected.

-> Live demo

Note that this would work with a self defined class:

class H {
  void f() {
    T(7); // fine iff H is a friend of T (otherwise "private within this context" error)
  }
};

So, to restate my question from above, should this work?

bitmask
  • 32,434
  • 14
  • 99
  • 159
  • 1
    These classes typically have requirements that client type must satisfy. These requirements typically never include any special rules for being friends with someone. e.g. "class should be default constructible", not "class should be default constructible when constructor is invoked from that particular place of standard library". Rewriting requirements in such a specific manner would certainly complicate code, e.g force `std::is_default_constructible` to be reimplemented dozens of times. – user7860670 Feb 15 '21 at 18:34
  • @user7860670 Yes, in the case of `std::optional::emplace` cppreference states: *This overload only participates in overload resolution if `std::is_constructible&, Args&&...>::value` is true.* But does so **ONLY** for the `initializer_list` version. For the other one, no such requirement is expressed (at least over at cppreference). – bitmask Feb 15 '21 at 18:44
  • And yes, I agree that this would likely complicate code for the reason you mention. – bitmask Feb 15 '21 at 18:45
  • 1
    cppreference states *that T must be constructible from Args... for overload (1)* – user7860670 Feb 15 '21 at 18:45
  • @user7860670 Well, it doesn't state for whom, if you want to be nitpicky. – bitmask Feb 15 '21 at 19:17
  • *Requires: is_constructible_v is true.* - from standard – user7860670 Feb 15 '21 at 19:23

1 Answers1

0

However, my reasoning was that if I befriend said container, various emplace's should be allowed

That might result in certain code duplications or code that needs to be move to a place where it probably should not be.

e.g. std::queue itself is just a wrapper, that forwards the member function class to the used container (which is std::deque<T> by default), so either std::queue needs to construct that object or you also need to make std::deque<T> fried, same problem is with move and copy constructors.

Another case could be std::list which might forward the construction to the node type.

And if you make an exception for one type like std::optional then this won't be consistent throughout the std lib.

t.niese
  • 39,256
  • 9
  • 74
  • 101