我有以下示例代码:
#include <iostream>
#include <initializer_list>
struct foo { int x{}; };
struct bar { double y{}; };
template<typename... Base>
struct Foo : Base...
{
constexpr auto init_int(int x) const
{ return Foo<foo, Base...>{foo{x}, static_cast<Base>(*this)...}; }
constexpr auto init_double(double x) const
{ return Foo<bar, Base...>{bar{x}, static_cast<Base>(*this)...}; }
};
int main()
{
constexpr auto f = Foo<>{}.init_double(.5).init_int(1234);
static_assert(f.x == 1234);
static_assert(f.y == 0.5);
}
这似乎在最近的Clang和GCC(使用-std = c ++ 1z)中都有效,但在MSVC2017中却没有。所以我不确定这是合法的C ++:它似乎合成了一个看起来像这样的构造函数:
Foo(Base1 b1, Base2 b2, Base3 b3, ...):
Base1{b1},
Base1{b2},
Base3{b3},
... { }
我可以安全地做到这一点,并确保它定义明确吗?
答案 0 :(得分:3)
从C ++ 17开始,聚合可以具有聚合类型的公共非虚拟基类。 foo
和bar
是聚合,因此Foo<foo,bar>
是聚合,因此在大括号初始化时会使用聚合初始化。
因此,代码在C ++ 17及更高版本中是合法的(它不是之前的代码)。请注意,没有合成“聚合构造函数”,只是大括号初始化对聚合有一个特殊的解释。
BTW,我在这里看不到CRTP(Foo不会继承具有模板参数的模板特化,具体取决于超类型)