8

Originated from this CodeReview topic:

#include <cstddef>
#include <algorithm>
#include <iostream>
#include <type_traits>
#include <utility>

template <typename T>
class aggregate_wrapper : public T {
private:
  using base = T;

public:
  using aggregate_type = T;

  template <typename... Ts>
  aggregate_wrapper(Ts&&... xs)
      : base{std::forward<Ts>(xs)...} {
    // nop
  }
};

struct foo_t {
  foo_t(int) {}  
};

int main() {
  std::cout << std::is_constructible<foo_t>::value << std::endl;
  std::cout << std::is_constructible<aggregate_wrapper<foo_t>>::value << std::endl;
  // aggregate_wrapper<foo_t> v; // won't compile
}

How could std::is_constructible<aggregate_wrapper<foo_t>>::value be true when aggregate_wrapper<foo_t> v; does not actually compile?

Community
  • 1
  • 1
Lingxi
  • 14,579
  • 2
  • 37
  • 93
  • Perhaps you're mentally conflating *constructible* with *default constructible* ? An `aggregate_wrapper` can certainly be constructed, just not via `aggregate_wrapper v;` . – M.M Jan 25 '16 at 22:27
  • @M.M Nope, `is_default_constructible` and `is_constructible` (which means that `Args...` is an empty pack) are equivalent. – T.C. Jan 25 '16 at 22:57
  • Not sure why this was reopened; the dup is directly on point. – T.C. Jan 25 '16 at 23:01
  • @T.C. : Purely by accident! – ildjarn Jan 26 '16 at 01:30

1 Answers1

3

In the C++ standard, in the is_constructible description, there is this innocent-looking quote:

Only the validity of the immediate context of the [imaginary v] variable initialization is considered.

And then a note explaining what it means:

The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and function template specializations, the generation of implicitly-defined functions, and so on. Such side effects are not in the immediate context and can result in the program being ill-formed.

My interpretation is that when you write:

aggregate_wrapper<foo_t> v;

You are using the default constructor of aggregate_wrapper, it exists and it is accesible, so it succeeds, at least in the immediate context. Then, the non-immediate context includes the body of the constructor, and that fails, but that does not change the result of is_constructible.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • Then I guess `std::is_constructible` is not that useful anyway. Would rather write one myself that goes beyond this immediate context. – Lingxi Jan 26 '16 at 02:04
  • The real question should be why `std::is_constructible` stops at the `immediate context`? – Lingxi Jan 26 '16 at 02:09