为什么名字是';在初始值设定项之前的声明要点是什么? < > C++标准:
名称的声明点紧跟在其完整声明符之后,在其完整声明符之前 初始值设定项(如果有)。。。[基本.范围.pdecl] 也就是说,变量在其自身初始化表达式的上下文中处于作用域中,并且可以被引用 据我所知,你可以用它做以下几类事情:为什么名字是';在初始值设定项之前的声明要点是什么? < > C++标准:,c++,language-lawyer,C++,Language Lawyer,名称的声明点紧跟在其完整声明符之后,在其完整声明符之前 初始值设定项(如果有)。。。[基本.范围.pdecl] 也就是说,变量在其自身初始化表达式的上下文中处于作用域中,并且可以被引用 据我所知,你可以用它做以下几类事情: int x=x,格式良好但无意义 void*p=&p,很可爱但没用 std::anya{&a},#2的C++17版本 MyClass m{std::move(m)},是#1的C++11版本,可能是UB MyClass m{myFunc(m)},有一个函数可以接受未初始化的对象
int x=x
,格式良好但无意义void*p=&p
,很可爱但没用std::anya{&a}
,#2的C++17版本MyClass m{std::move(m)}
,是#1的C++11版本,可能是UBMyClass m{myFunc(m)}
,有一个函数可以接受未初始化的对象,我想它会记录在什么地方?并返回一些值,以便构造函数可以尝试int-avg=avg(a,b,c)
。这不是好代码,也不是任何东西都必需的,但它比void*p=&p
更有意义。)
但这不仅仅是用例。通常,C++会努力阻止对未初始化对象的访问。例如,它一次设置一个对象的vtable基类:如果D从C继承从B继承,在C的构造函数中,虚拟方法将被分派到C的实现,而不是D的实现。可以窥视未初始化对象的常见情况是这种情况,以及(更常见的问题是)在成员初始值设定项表达式中使用this
因此,我看不出将名称放在初始值设定项之前的作用域中有什么用处,我可以看到将其延迟到初始值设定项之后的明确理由(Stroustrup也会看到)。考虑到这一点,C++选择的行为是否有明确的意义
它的地址通常对它的初始化并不重要
模板
结构体类型
{
节点*prev,*next;
std::可选数据;
};
模板
结构列表
{
节点哨兵{&sentinel,&sentinel};
//方法。。。
};
这是一个完全合法和有用的基于哨兵的双链接列表的初始化
任何类似图的数据结构都可以有自引用,可以通过将对象的指针或引用作为参数传递给自己的构造函数来实现。没有理由不允许这样做
如果您担心
int x=x中固有的未定义行为代码>,就像你应该做的那样。编译器现在相当擅长捕捉这些东西。我不是说这是C语言的基本原理,而是int*p=malloc(5*sizeof(*p))代码>。有趣的问题。我认为标准引用的语句并没有将声明仅限于变量。您可以轻松合法地声明函数template auto foo()->decltype(T::foo())代码>我不确定这在哪里有用,但它是有效的。int*arr=malloc(10*sizeof(*arr))代码>是一个有效的用例。在Windows API中,有时会在结构中存储结构的大小。所以,它确实有它的用例。@AyxanHaqverdili Chris提到了同样的事情。不过,在winapi的例子中,我看到的所有示例代码都使用sizeof(TYPE)
,而不是sizeof(obj)
。不是一个必要的用例,除非需要向后兼容。@Sneftel你说只有向后兼容才需要它是什么意思?如果您现在正在编写win32代码,您仍然会执行类似的操作。我喜欢sizeof(obj)
,因为您不需要重复类型名称。我不是说规则是好的。我只是说,它确实有一些用例。关于更复杂的自我引用,这是一个很好的观点。然而,FWIW,GCC-Wall-Wextra接受MyClass m{std::move(m)}
而没有任何抱怨。
template <class T>
struct Node
{
Node *prev, *next;
std::optional<T> data;
};
template <class T>
struct List
{
Node<int> sentinel {&sentinel, &sentinel};
// methods ...
};