为什么在将 const 指针初始化为 null 时没有警告或错误?
Why is there no warning or error when initializing a const pointer to null?
这可能是一个非常基本的问题,但我试图在 SO 中找到答案,但找不到确切的答案。
用nullptr
初始化一个const
指针有什么意义?
int *const pi = nullptr;
为什么编译器没有发出警告或错误,为什么指针 pi
不能在程序的任何地方有效使用?
我试过用g++ -w
编译这个。
此外,您能否给出一些用例,其中 const
指针将在实际代码中出于有效目的初始化为 nullptr
?
不要混淆 const
,意思是:“初始化后值不能改变”和“在任何情况下这个值总是 nullptr
”。我的意思是:
struct foo {
int *const pi = nullptr;
foo( int* p) : pi(p) {}
};
foo
的不同实例对于 pi
可以有不同的值。
另一种初始化条件的方法是:
int *const pi = (some_condition) ? nullptr : some_pointer;
即使是普通的
int *const pi = nullptr;
没有错。它基本上是给 nullptr
一个不同的名称,因此您可以在任何可以使用 nullptr
的地方使用 pi
。例如作为容器中的哨兵:
int * const empty = nullptr;
for ( int* element : container) {
if (element != empty) do_something(element);
}
这可能被认为是混淆,直接使用 nullptr
会更具可读性。尽管稍后考虑哨兵值的变化,然后使用 empty
make 很容易替换它的所有出现。
虽然我同意初始化指向 nullptr
的 const
指针 通常 特别有用,但有一种情况 可能 我合适的是 有条件地 定义 const pi
指向 nullptr
或其他(有效)地址的指针,具体取决于编译-时间设置,如下例所示。 (const
限定符可防止其他代码无意中更改值,从而破坏上述编译时条件。)
#include<iostream>
#define USENULL 1 // Comment out this line to get a 'valid' pointer!
int main()
{
int a = 42;
#ifdef USENULL
int* const pi = nullptr;
#else
int* const pi = &a;
#endif
int* pa = pi;
if (pa) std::cout << *pa;
else std::cout << "null pointer";
std::cout << std::endl;
return 0;
}
例如,当您需要区分调试版本和发布版本时,可能会出现这种情况。
我将尝试概括@AdrianMole 的(第二个)示例:
有时,一段代码是更通用指令或模式的退化案例或离群案例。因此:
在@AdrianMole 的示例中,有一个源外参数控制 pi
是否应该具有有意义的值。你不想为了适应这种情况而扭曲你的源代码太多,所以你保留相同的代码但使用 nullptr
,可能稍后(运行-时间)检查你是否有没有nullptr
.
您可能有一个模板 class,其中的一般定义类似于:
template <typename T>
class A {
int *const pi = whatever<T>();
}
具有专业化
template <>
class A<foo_t> {
int *const pi = nullptr;
}
因为您想避免调用 whatever<foo>()
.
很可能可以改进此类代码以避免显式 nullptr
赋值。但是通过警告来迫使开发人员这样做有点过分了。
(错误是不可能的,因为就语言标准而言,它是完全有效的代码。)
这可能是一个非常基本的问题,但我试图在 SO 中找到答案,但找不到确切的答案。
用nullptr
初始化一个const
指针有什么意义?
int *const pi = nullptr;
为什么编译器没有发出警告或错误,为什么指针 pi
不能在程序的任何地方有效使用?
我试过用g++ -w
编译这个。
此外,您能否给出一些用例,其中 const
指针将在实际代码中出于有效目的初始化为 nullptr
?
不要混淆 const
,意思是:“初始化后值不能改变”和“在任何情况下这个值总是 nullptr
”。我的意思是:
struct foo {
int *const pi = nullptr;
foo( int* p) : pi(p) {}
};
foo
的不同实例对于 pi
可以有不同的值。
另一种初始化条件的方法是:
int *const pi = (some_condition) ? nullptr : some_pointer;
即使是普通的
int *const pi = nullptr;
没有错。它基本上是给 nullptr
一个不同的名称,因此您可以在任何可以使用 nullptr
的地方使用 pi
。例如作为容器中的哨兵:
int * const empty = nullptr;
for ( int* element : container) {
if (element != empty) do_something(element);
}
这可能被认为是混淆,直接使用 nullptr
会更具可读性。尽管稍后考虑哨兵值的变化,然后使用 empty
make 很容易替换它的所有出现。
虽然我同意初始化指向 nullptr
的 const
指针 通常 特别有用,但有一种情况 可能 我合适的是 有条件地 定义 const pi
指向 nullptr
或其他(有效)地址的指针,具体取决于编译-时间设置,如下例所示。 (const
限定符可防止其他代码无意中更改值,从而破坏上述编译时条件。)
#include<iostream>
#define USENULL 1 // Comment out this line to get a 'valid' pointer!
int main()
{
int a = 42;
#ifdef USENULL
int* const pi = nullptr;
#else
int* const pi = &a;
#endif
int* pa = pi;
if (pa) std::cout << *pa;
else std::cout << "null pointer";
std::cout << std::endl;
return 0;
}
例如,当您需要区分调试版本和发布版本时,可能会出现这种情况。
我将尝试概括@AdrianMole 的(第二个)示例:
有时,一段代码是更通用指令或模式的退化案例或离群案例。因此:
在@AdrianMole 的示例中,有一个源外参数控制
pi
是否应该具有有意义的值。你不想为了适应这种情况而扭曲你的源代码太多,所以你保留相同的代码但使用nullptr
,可能稍后(运行-时间)检查你是否有没有nullptr
.您可能有一个模板 class,其中的一般定义类似于:
template <typename T> class A { int *const pi = whatever<T>(); }
具有专业化
template <> class A<foo_t> { int *const pi = nullptr; }
因为您想避免调用
whatever<foo>()
.
很可能可以改进此类代码以避免显式 nullptr
赋值。但是通过警告来迫使开发人员这样做有点过分了。
(错误是不可能的,因为就语言标准而言,它是完全有效的代码。)