我今天正在调试一个失败的clang构建。由于is_default_constructible
已评估为false
,因此该构建基本上已破坏。经过几个小时的平分问题后,我将问题减少到最小的情况:
#include <type_traits>
#include <string>
namespace A {
// This has been extracted from an old (outdated(?))
// implementation of an optional type
struct empty_t {};
template<class T>
struct o
{
public:
template<class... U,
typename std::enable_if<
std::is_constructible<T, U...>::value,
bool
>::type = false
>
o(empty_t, U&&... u) { }
};
}
struct B
{
struct Foo
{
//Foo () {}; // uncomment this line and it works
bool x = true; // comment this line and it works
};
std::string c; // comment this line and it works, also change to int
A::o<Foo> r; // comment this line and it works
static const bool b;
};
static_assert(
std::is_default_constructible<B::Foo>::value,
"not constructible!");
上面的例子用g ++ 6.3和7.0编译得很好。它在clang ++ 4.0.0和3.9.1中失败了 - 静态断言仅在非常具体的构造中失败,但它仍然破坏了我们的构建。正如您可以自己尝试一样,一些最小的更改可以解决问题(例如,评论其中一条提到的行)。结果看起来有点武断。
我想知道的是,clang中的明显错误是否实际上是一个错误或一些未定义的行为。实际上,这部分语言的定义有多好?
我也很感激有关如何调试这些问题的任何建议:有没有一种好方法可以从clang中获取一些信息,为什么它确实认为Foo不是默认的可构造的?
最后,如果你们中的任何人都可以访问(符合标准的)第三个C ++实现并且可以报告结果,那将非常好。
答案 0 :(得分:0)
在外部类完成之前(即,在外部类的定义的分号处),不会实例化嵌套类的默认数据成员初始值设定项。当使用类型特征在完成时(即在其定义之后)但在外部类完成之前查询嵌套类时,这会导致奇怪的后果。
你可以通过在B外移动Foo来解决这个问题。