此代码是否应该无法在C ++ 17中编译? [英] Should this code fail to compile in C++17?

查看:134
本文介绍了此代码是否应该无法在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆