这段代码是否应该无法在 C++17 中编译?
Should this code fail to compile in 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 的正确行为更改,还是一个 clang 错误?如果它是正确的,为什么它现在在 C++17 中无效(或者它可能一直是,但 clang 只是在早期的标准修订版中允许它)?
clang 在这里是正确的。这是一个简化的例子:
struct B {
protected:
B() { }
};
struct D : B { };
auto d = D{};
在 C++14 中,D
不是 聚合,因为它有基数 class,所以 D{}
是 "normal" (non-aggregate) 初始化调用 D
的默认构造函数,后者又调用 B
的默认构造函数。这很好,因为 D
可以访问 B
的默认构造函数。
在 C++17 中,聚合的定义被拓宽了——现在允许基数 classes(只要它们不是 virtual
)。 D
现在是聚合,也就是说 D{}
是聚合初始化。在 aggregate-initialization 中,这意味着我们(调用者)正在初始化所有子对象——包括基础 class 子对象。但是我们没有可以访问B
的构造函数(它是protected
),所以我们不能调用它,所以它是ill-formed。
不用担心,修复很简单。使用括号:
auto d = D();
这又回到了像以前一样调用 D
的默认构造函数。
我正在更新一个项目以使用 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 的正确行为更改,还是一个 clang 错误?如果它是正确的,为什么它现在在 C++17 中无效(或者它可能一直是,但 clang 只是在早期的标准修订版中允许它)?
clang 在这里是正确的。这是一个简化的例子:
struct B {
protected:
B() { }
};
struct D : B { };
auto d = D{};
在 C++14 中,D
不是 聚合,因为它有基数 class,所以 D{}
是 "normal" (non-aggregate) 初始化调用 D
的默认构造函数,后者又调用 B
的默认构造函数。这很好,因为 D
可以访问 B
的默认构造函数。
在 C++17 中,聚合的定义被拓宽了——现在允许基数 classes(只要它们不是 virtual
)。 D
现在是聚合,也就是说 D{}
是聚合初始化。在 aggregate-initialization 中,这意味着我们(调用者)正在初始化所有子对象——包括基础 class 子对象。但是我们没有可以访问B
的构造函数(它是protected
),所以我们不能调用它,所以它是ill-formed。
不用担心,修复很简单。使用括号:
auto d = D();
这又回到了像以前一样调用 D
的默认构造函数。