此代码是否应该无法在C ++ 17中编译? [英] Should this code fail to compile in C++17?
问题描述
我正在更新一个项目以使用C ++ 17,发现一些实例,遵循该模式的代码在最新版本的clang上导致了编译错误:
I was updating a project to use C++17 and found a few instances where code that followed this pattern was causing a compile error on recent versions of clang:
#include <boost/variant.hpp>
struct vis : public boost::static_visitor<void>
{
void operator()(int) const { }
};
int main()
{
boost::variant<int> v = 0;
boost::apply_visitor(vis{}, v);
}
在C ++ 17模式下使用clang v8.0,此操作将失败,并出现以下错误:
<source>:11:30: error: temporary of type 'boost::static_visitor<void>' has protected destructor
boost::apply_visitor(vis{}, v);
^
/opt/compiler-explorer/libs/boost_1_64_0/boost/variant/static_visitor.hpp:53:5: note: declared protected here
~static_visitor() = default;
但是,它可以在C ++ 14模式下干净地编译。我发现,如果将括号初始化 vis {}
更改为括号 vis()
,则在这两种语言中都可以正确编译模式。我尝试过的每个版本的gcc都允许在C ++ 17模式下使用两种变体。
However, it compiles cleanly in C++14 mode. I found that if I change the brace initialization vis{}
to parentheses vis()
, then it compiles correctly in both modes. Every version of gcc that I've tried allows both variants in C++17 mode.
这是从C ++ 14到C ++的正确行为更改吗? 17,或者这是一个lang虫?如果正确,为什么现在在C ++ 17中无效(或者也许一直如此,但是clang仅在较早的标准版本中允许使用)?
Is this a correct change in behavior from C++14 to C++17, or is this a clang bug? If it is correct, why is it now invalid in C++17 (or maybe it always was, but clang just allows it in earlier standard revisions)?
推荐答案
clang在这里是正确的。这是一个简化的示例:
clang is correct here. Here's a reduced example:
struct B {
protected:
B() { }
};
struct D : B { };
auto d = D{};
在C ++ 14中, D
是不是聚合,因为它具有基类,因此 D {}
是普通(非聚合)初始化,它调用 D
的默认构造函数,它依次调用 B
的默认构造函数。这很好,因为 D
可以访问 B
的默认构造函数。
In C++14, D
is not an aggregate because it has a base class, so D{}
is "normal" (non-aggregate) initialization which invokes D
's default constructor, which in turn invokes B
's default constructor. This is fine, because D
has access to B
's default constructor.
在C ++ 17中,对聚合的定义进行了扩展-现在允许基类(只要它们不是非虚拟
)。 D
现在是一个聚合,这意味着 D {}
是聚合初始化。在聚合初始化中,这意味着我们(调用方)正在初始化所有子对象-包括基类子对象。但是我们没有可以访问 B
的构造函数(受保护 c)
In C++17, the definition of aggregate was widened - base classes are now allowed (as long as they're non-virtual
). D
is now an aggregate, which means that D{}
is aggregate initialization. And in aggregate-initialization, this means that we (the caller) are initializing all the subobjects - including the base class subobject. But we do not have access to B
's constructor (it is protected
), so we cannot invoke it, so it is ill-formed.
请不要担心,此修复很容易。使用括号:
Fear not, the fix is easy. Use parentheses:
auto d = D();
这可以回溯到调用 D
默认的构造函数和以前一样
This goes back to invoking D
's default constructor as before.
这篇关于此代码是否应该无法在C ++ 17中编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!