其他分享
首页 > 其他分享> > 该代码是否无法在C 17中编译?

该代码是否无法在C 17中编译?

作者:互联网

我正在更新一个项目以使用C 17,并发现一些实例,其中遵循此模式的代码在最近版本的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);
}

Using clang v8.0 in C++17 mode, this fails with the following error

<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;

但是,it compiles cleanly in C++14 mode.我发现如果我将括号初始化vis {}更改为括号vis(),那么它在两种模式下都能正确编译.我尝试过的每个版本的gcc都允许C 17模式中的两种变体.

这是从C 14到C 17的行为的正确变化,还是这是一个铿锵的错误?如果它是正确的,为什么它现在在C 17中无效(或者它可能总是如此,但是clang只是在早期的标准版本中允许它)?

解决方法:

铿锵在这里是对的.这是一个简化的例子:

struct B {
protected:
    B() { }
};

struct D : B { };

auto d = D{};

在C 14中,D不是聚合,因为它有一个基类,所以D {}是“正常”(非聚合)初始化,它调用D的默认构造函数,后者又调用B的默认构造函数.这很好,因为D可以访问B的默认构造函数.

在C17中,扩展了聚合的定义 – 现在允许基类(只要它们是非虚拟的). D现在是一个聚合,这意味着D {}是聚合初始化.在聚合初始化中,这意味着我们(调用者)正在初始化所有子对象 – 包括基类子对象.但我们无法访问B的构造函数(它受到保护),因此我们无法调用它,因此它是不正确的.

不用担心,修复很容易.使用括号:

auto d = D();

这可以像以前一样调用D的默认构造函数.

标签:c,clang,c17,boost-variant
来源: https://codeday.me/bug/20191006/1860089.html