Search code examples
c++language-lawyerc++17type-traits

std::is_constructible immediate context and friend declarations


Recently I attempted to detect existence of specific private constructors and ran into the issue that std::is_constructible only checks the immediate context and thus would not recognize anything such constructors. After some research I did see that one answer here mentioned that the proper way is to friend the class in question with std::is_constructible to allow it to have access.

To test if this method worked, I attempted the following test code

#include <type_traits>

class A {
private:
  template<typename, typename ...>
  friend struct std::is_constructible;

  A() = default;
};

int main() {
  static_assert(std::is_constructible<A>::value);
  static_assert(std::is_constructible_v<A>);
}

Interestingly, this method did appear to work with std::is_constructible in Visual Studio 2017, though I then began to encounter issues with std::is_constructible_v. In their implementation, rather than using an alias template to the actual struct std::is_constructible itself, they instead call the intrinsic used internally directly which in turn ignores the friend declaration.

Thinking that this was a bug with their standard library implementation, I then tested in other compilers and found that neither clang nor gcc were able to pass this assertion in any case, causing me to wonder if it is even supposed to work like this at all (some of the comments on the linked post seem to suggest it's a bug while others say it should not take friend declarations into account).

Thus, the main question is should this code work correctly (as in should it be able to access the private constructor and pass the assertions) with way the standard defines the access being limited to an immediate context? A similar question was also posed here, the main thing I am unsure about is the exact definition intended by an "immediate context" in this case since this is slightly different than the example in the linked question.

A relevant passage from N4713 23.15.4.3.8 [meta.unary.prop]/8:

Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered.


Solution

  • After some research I did see that one answer here mentioned that the proper way is to friend the class in question with std::is_constructible to allow it to have access.

    No. Definitely not. This is not the proper way at all. There is no guarantee whatsoever that this will work.

    Moreover, there is no guarantee whatsoever that friending anything in the standard library will do what you want it to. The standard library is not your friend. P1339 was approved in Kona and will update SD-8 to give the standard library the right to assume that users do not do this... and the library will not care if changes break user friendship like this.

    std::is_constructible_v<A> is, and should be, false.