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?
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With