Open Marc-Pierre-Barbier opened 2 months ago
interestingly, if i copy the definition if is_constructible_v from gcc10 and 13 i get working functions.
namespace notstd {
template <typename _Tp, typename... _Args>
inline constexpr bool is_constructible_v13 = __is_constructible(_Tp, _Args...);
template <typename _Tp, typename... _Args>
inline constexpr bool is_constructible_v10 =
std::is_constructible<_Tp, _Args...>::value;
}
still the import using
@llvm/issue-subscribers-clang-frontend
Author: Marc barbier (Marc-Pierre-Barbier)
Related: #36032 / CWG1351
The reason this happens is that optional
instantiates is_constructible_v<A::B>
. To check this, the compiler needs to instantiate the implicitly declared default constructor. The noexcept
specifier of that depends on the initialiser, which has been delayed until a complete class context. Since it's not available, Clang reports false
(perhaps it should error here instead?). Templates are cached, so is_constructible_v<A::B>
remains false
.
It has to be delayed because the member initializer in the nested class is a complete class context of the enclosing class as well (https://eel.is/c++draft/class.mem.general#note-5), so it can't be parsed until the enclosing class is actually complete:
struct X {
struct Y {
int mem = /* complete-class context of X, cannot parse before every member of `X` is parsed */ later_declared;
};
static constexpr int later_declared = 4; // Available in the default member initializer
};
It's only the noexcept
specifier calculation that's a problem: https://godbolt.org/z/q34c8hdx7
#include <optional>
class A {
public:
class B {
public:
constexpr B() noexcept = default; // Don't need the default initializer anymore
double example{};
};
std::optional<B> transmission{};
};
int main(int argc, char ** argv) {
static_assert(std::is_constructible<A::B>::value);
static_assert(std::is_default_constructible_v<A::B>);
static_assert(std::is_constructible_v<A::B>);
std::optional<A::B> a;
a.emplace();
return 0;
}
Though this might be a Clang bug: the implicit exception specification might need to be delayed until a complete class context, like how explicit exception specifications are delayed. (So is_constructible_v
would be true
and is_nothrow_constructible_v
would error exception specification is not available until end of class definition
)
Hello,
while trying to migrate one of my apps to clang i discovered the following issue.
i have a class A contained within class B. class A has a
std::optional<B>
as a member value. class B contains a double that i want initialise to 0. the simples option is to use =0 or {} instead of writing a constructor. but when i do that combined with thestd::optional<B>
any usage ofstd::optional<B>::emplace(not limited to the member variable)
will fail to compile. This is because B is no longer constructible according tois_constructible_v
but it still according tois_default_constructible_v
. The weirdest part ? commenting outstd::optional<B>
will allow the code to compile alternatively defining a default contructor in B that do the initialisation of the double will also fix it.Tested with the following code using clang 17(using gcc10)