5

Originated from this topic. Also related to this topic.

My question is why std::is_constructible stops at the immediate context? I think users of std::is_constructible would expect it to work in full depth and give an exact answer. With this immediate context thing, you may have std::is_constructible give you a green light, only to get a hard compiler error when you actually do it. Doesn't this defeat the original goal and purpose of std::is_constructible. Now, it basically looks useless to me. I guess std::looks_constructible_at_first_sight would be a better name for the current semantics :(

Community
  • 1
  • 1
Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • It reduces to the [Reachability problem](https://en.wikipedia.org/wiki/Reachability_problem), which is NP-hard. – erip Jan 26 '16 at 02:42
  • @erip Wow~ So, it's basically not that we don't want to do it, but we have a technical issue here? But I think simple SFINAE would just do the job. No? – Lingxi Jan 26 '16 at 02:46
  • 3
    SFINAE also stops at the immediate context. – T.C. Jan 26 '16 at 02:49
  • @Lingxi One of the links above describes the immediate context as it applies to SFINAE. – erip Jan 26 '16 at 02:51
  • 1
    @T.C. But eventually, the compiler will work in full depth and come up with an exact answer. Can we take advantage of this fact? – Lingxi Jan 26 '16 at 02:58
  • 2
    First, you probably *don't* want overload resolution to "work in full depth". Compile time would skyrocket. Second, existing implementations may not be capable of gracefully recovering from an error deep in the instantiation stack. Just look at MSVC's struggles with expression SFINAE. – T.C. Jan 26 '16 at 09:19

1 Answers1

2

If your constructor's signature is far more permissive than it should be, that's the issue - not is_constructible's implementation. In your original example,

template <typename... Ts, typename=decltype(base{std::declval<Ts>()...})>
aggregate_wrapper(Ts&&... xs)
    : base{std::forward<Ts>(xs)...} {/*…*/}

does the job. If is_constructible "spuriously" gives a green light, so might your constructor template spuriously be selected over other constructors, because overload resolution finds it to be the optimal match.

However, overload resolution isn't designed to give only true negatives/positives: It is designed to find the best match given appropriate arguments, or yield nothing if the arguments are sufficiently inappropriate. is_constructible may be superficial in some sense, but that's what traits are for - checking signatures, which are an entity's representation in the realms of overload resolution and SFINAE - not preventing that your function templates accept everything but only actually instantiate soundly for a small margin of arguments.
That's your responsibility, and if you meet it, you will both get proper results from is_constructible and efficient compilation. Which is definitely better than unfailing operation of is_constructible and high compilation times with multitudinous, hidden rules in calls to function templates.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Is `base{std::declval()...}` equivalent to `base{std::forward(std::declval())...}`? – Lingxi Jan 26 '16 at 12:08
  • 1
    @Lingxi Yes. `declval` returns `add_rvalue_reference`, which yields the exact same types as [`forward`](http://en.cppreference.com/w/cpp/utility/forward) does (check its return type!). – Columbo Jan 26 '16 at 12:11
  • I don't know where your code snippet came from, but that solved my problem! Thanks! – ThreeStarProgrammer57 Oct 02 '19 at 23:55