学习一下关于C++基础知识nodiscard。

1介绍

nodiscard是c++17引入的一种标记符,其语法一般为[[nodiscard]]或[nodiscard(“string”)],含义可以理解为“不应舍弃”。nodiscard一般用于标记函数的返回值或者某个类,当使用某个弃值表达式而不是cast to void 来调用相关函数时,编译器会发出相关warning。

2实例

2.1出现警告

 1#include <iostream>
 2using namespace std;
 3
 4[[nodiscard]] int calculateSum(int a, int b) {
 5    return a + b;
 6}
 7
 8int main() {
 9    calculateSum(2, 3);  // 没有使用返回值,可能会产生警告或错误
10	cout << 1 << endl;
11    return 0;
12}

如果在真实clion编译结果是没有异常,有一个警告。

如果是在线编译器,同样也会提示一个警告

同样也适用于枚举类

 1#include <iostream>
 2using namespace std;
 3
 4[[nodiscard]] int fi()              //修饰函数返回值
 5{
 6    return 1;
 7}
 8
 9class [[nodiscard]] C{};            //修饰类
10enum class [[nodiscard]] E{e1, e2}; //修饰枚举类
11
12C fc()
13{
14    return C();
15}
16
17E fe()
18{
19    return E::e1;
20}
21
22int main()
23{
24    fi();    //没有使用fi的返回值,告警:ignoring return value of 'int fi()', declared with attribute nodiscard
25    fc();    //没有使用fc的返回值,告警:ignoring returned value of type 'C', declared with attribute nodiscard
26    fe();    //没有使用fe的返回值,告警:ignoring returned value of type 'E', declared with attribute nodiscard
27    return 0;
28}

结果

 1====================[ Build | CPPProject0710 | Debug ]==========================
 2"D:\Clion\CLion 2022.2.1\bin\cmake\win\bin\cmake.exe" --build D:\project\CPPProject0710\cmake-build-debug --target CPPProject0710 -j 3
 3[1/2] Building CXX object CMakeFiles/CPPProject0710.dir/main.cpp.obj
 4D:/project/CPPProject0710/main.cpp: In function 'int main()':
 5D:/project/CPPProject0710/main.cpp:23:7: warning: ignoring return value of 'int fi()', declared with attribute 'nodiscard' [-Wunused-result]
 6   23 |     fi();    //ignoring return value of 'int fi()', declared with attribute nodiscard
 7      |     ~~^~
 8D:/project/CPPProject0710/main.cpp:3:19: note: declared here
 9    3 | [[nodiscard]] int fi()              //Modifies the return value of the function
10      |                   ^~
11D:/project/CPPProject0710/main.cpp:24:7: warning: ignoring returned value of type 'C', declared with attribute 'nodiscard' [-Wunused-result]
12   24 |     fc();    //ignoring returned value of type 'C', declared with attribute nodiscard
13      |     ~~^~
14D:/project/CPPProject0710/main.cpp:11:3: note: in call to 'C fc()', declared here
15   11 | C fc()
16      |   ^~
17D:/project/CPPProject0710/main.cpp:8:21: note: 'C' declared here
18    8 | class [[nodiscard]] C{};            //Modifier class
19      |                     ^
20D:/project/CPPProject0710/main.cpp:25:7: warning: ignoring returned value of type 'E', declared with attribute 'nodiscard' [-Wunused-result]
21   25 |     fe();    //ignoring returned value of type 'E', declared with attribute nodiscard
22      |     ~~^~
23D:/project/CPPProject0710/main.cpp:16:3: note: in call to 'E fe()', declared here
24   16 | E fe()
25      |   ^~
26D:/project/CPPProject0710/main.cpp:9:26: note: 'E' declared here
27    9 | enum class [[nodiscard]] E{e1, e2}; //Modified enum class
28      |                          ^
29[2/2] Linking CXX executable CPPProject0710.exe
30
31Build finished

2.2去除warning方法

将上述改成cast to void或者接收一个返回值就不会出现警告

 1#include <iostream>
 2using namespace std;
 3[[nodiscard]] int fi()              //Modifies the return value of the function
 4{
 5    return 1;
 6}
 7
 8class [[nodiscard]] C{};            //Modifier class
 9enum class [[nodiscard]] E{e1, e2}; //Modified enum class
10
11C fc()
12{
13    return C();
14}
15
16E fe()
17{
18    return E::e1;
19}
20
21int main()
22{
23    //fi();    //ignoring return value of 'int fi()', declared with attribute nodiscard
24    int ret = fi();
25    static_cast<void>(fi());
26    //fc();    //ignoring returned value of type 'C', declared with attribute nodiscard
27    C c = fc();
28    static_cast<void>(fc());
29    //fe();    //ignoring returned value of type 'E', declared with attribute nodiscard
30    E e = fe();
31    static_cast<void>(fe());
32    return 0;
33}

输出

1====================[ Build | CPPProject0710 | Debug ]==========================
2"D:\Clion\CLion 2022.2.1\bin\cmake\win\bin\cmake.exe" --build D:\project\CPPProject0710\cmake-build-debug --target CPPProject0710 -j 3
3[1/2] Building CXX object CMakeFiles/CPPProject0710.dir/main.cpp.obj
4[2/2] Linking CXX executable CPPProject0710.exe
5
6Build finished

2.3nodiscard失效

 1#include <iostream>
 2using namespace std;
 3[[nodiscard]]int* fi()              //Modifies the return value of the function
 4{
 5    return new int(1);
 6}
 7
 8class [[nodiscard]] C{};            //Modifier class
 9enum class [[nodiscard]] E{e1, e2}; //Modified enum class
10
11C* fc()
12{
13    return new C();
14}
15
16E&& fe()
17{
18    return E::e1;
19}
20
21int main()
22{
23    //还是这段代码,这里虽然转化成指针形式,但是依然会出现warning
24    fi();    
25    fc();    
26    fe();   
27
28    return 0;
29}

结果

 1====================[ Build | CPPProject0710 | Debug ]==========================
 2"D:\Clion\CLion 2022.2.1\bin\cmake\win\bin\cmake.exe" --build D:\project\CPPProject0710\cmake-build-debug --target CPPProject0710 -j 3
 3[1/2] Building CXX object CMakeFiles/CPPProject0710.dir/main.cpp.obj
 4D:/project/CPPProject0710/main.cpp: In function 'E&& fe()':
 5D:/project/CPPProject0710/main.cpp:18:15: warning: returning reference to temporary [-Wreturn-local-addr]
 6   18 |     return E::e1;
 7      |            ~~~^~
 8D:/project/CPPProject0710/main.cpp: In function 'int main()':
 9D:/project/CPPProject0710/main.cpp:23:7: warning: ignoring return value of 'int* fi()', declared with attribute 'nodiscard' [-Wunused-result]
10   23 |     fi();    //ignoring return value of 'int fi()', declared with attribute nodiscard
11      |     ~~^~
12D:/project/CPPProject0710/main.cpp:3:19: note: declared here
13    3 | [[nodiscard]]int* fi()              //Modifies the return value of the function
14      |                   ^~
15[2/2] Linking CXX executable CPPProject0710.exe
16
17Build finished

上面显示只有fi()函数返回值存在[[nodiscard]]的warning操作,剩余E::e1的警告是出现了局部变量的引用,望读者不要这样写,这里只是为了举例说明。即类对象的引用和指针是可以做到去除[[nodiscard]]的warning操作的,普通函数的加引用或者指针依然会出现warning警告。

3.4在源码中举例

  1//frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.h
  2namespace HWC2 {
  3    class Display {
  4public:
  5    virtual ~Display();
  6
  7    virtual hal::HWDisplayId getId() const = 0;
  8    virtual bool isConnected() const = 0;
  9    virtual void setConnected(bool connected) = 0; // For use by Device only
 10    virtual bool hasCapability(
 11            aidl::android::hardware::graphics::composer3::DisplayCapability) const = 0;
 12    virtual bool isVsyncPeriodSwitchSupported() const = 0;
 13    virtual bool hasDisplayIdleTimerCapability() const = 0;
 14    virtual void onLayerDestroyed(hal::HWLayerId layerId) = 0;
 15
 16    [[nodiscard]] virtual hal::Error acceptChanges() = 0;
 17    [[nodiscard]] virtual base::expected<std::shared_ptr<HWC2::Layer>, hal::Error>
 18    createLayer() = 0;
 19    [[nodiscard]] virtual hal::Error getChangedCompositionTypes(
 20            std::unordered_map<Layer*, aidl::android::hardware::graphics::composer3::Composition>*
 21                    outTypes) = 0;
 22    [[nodiscard]] virtual hal::Error getColorModes(std::vector<hal::ColorMode>* outModes) const = 0;
 23    // Returns a bitmask which contains HdrMetadata::Type::*.
 24    [[nodiscard]] virtual int32_t getSupportedPerFrameMetadata() const = 0;
 25    [[nodiscard]] virtual hal::Error getRenderIntents(
 26            hal::ColorMode colorMode, std::vector<hal::RenderIntent>* outRenderIntents) const = 0;
 27    [[nodiscard]] virtual hal::Error getDataspaceSaturationMatrix(hal::Dataspace dataspace,
 28                                                                  android::mat4* outMatrix) = 0;
 29
 30    [[nodiscard]] virtual hal::Error getName(std::string* outName) const = 0;
 31    [[nodiscard]] virtual hal::Error getRequests(
 32            hal::DisplayRequest* outDisplayRequests,
 33            std::unordered_map<Layer*, hal::LayerRequest>* outLayerRequests) = 0;
 34    [[nodiscard]] virtual hal::Error getConnectionType(ui::DisplayConnectionType*) const = 0;
 35    [[nodiscard]] virtual hal::Error supportsDoze(bool* outSupport) const = 0;
 36    [[nodiscard]] virtual hal::Error getHdrCapabilities(
 37            android::HdrCapabilities* outCapabilities) const = 0;
 38    [[nodiscard]] virtual hal::Error getDisplayedContentSamplingAttributes(
 39            hal::PixelFormat* outFormat, hal::Dataspace* outDataspace,
 40            uint8_t* outComponentMask) const = 0;
 41    [[nodiscard]] virtual hal::Error setDisplayContentSamplingEnabled(bool enabled,
 42                                                                      uint8_t componentMask,
 43                                                                      uint64_t maxFrames) const = 0;
 44    [[nodiscard]] virtual hal::Error getDisplayedContentSample(
 45            uint64_t maxFrames, uint64_t timestamp,
 46            android::DisplayedFrameStats* outStats) const = 0;
 47    [[nodiscard]] virtual hal::Error getReleaseFences(
 48            std::unordered_map<Layer*, android::sp<android::Fence>>* outFences) const = 0;
 49    [[nodiscard]] virtual hal::Error present(android::sp<android::Fence>* outPresentFence) = 0;
 50    [[nodiscard]] virtual hal::Error setClientTarget(
 51            uint32_t slot, const android::sp<android::GraphicBuffer>& target,
 52            const android::sp<android::Fence>& acquireFence, hal::Dataspace dataspace) = 0;
 53    [[nodiscard]] virtual hal::Error setColorMode(hal::ColorMode mode,
 54                                                  hal::RenderIntent renderIntent) = 0;
 55    [[nodiscard]] virtual hal::Error setColorTransform(const android::mat4& matrix) = 0;
 56    [[nodiscard]] virtual hal::Error setOutputBuffer(
 57            const android::sp<android::GraphicBuffer>& buffer,
 58            const android::sp<android::Fence>& releaseFence) = 0;
 59    [[nodiscard]] virtual hal::Error setPowerMode(hal::PowerMode mode) = 0;
 60    [[nodiscard]] virtual hal::Error setVsyncEnabled(hal::Vsync enabled) = 0;
 61    [[nodiscard]] virtual hal::Error validate(nsecs_t expectedPresentTime, uint32_t* outNumTypes,
 62                                              uint32_t* outNumRequests) = 0;
 63    [[nodiscard]] virtual hal::Error presentOrValidate(nsecs_t expectedPresentTime,
 64                                                       uint32_t* outNumTypes,
 65                                                       uint32_t* outNumRequests,
 66                                                       android::sp<android::Fence>* outPresentFence,
 67                                                       uint32_t* state) = 0;
 68    [[nodiscard]] virtual ftl::Future<hal::Error> setDisplayBrightness(
 69            float brightness, float brightnessNits,
 70            const Hwc2::Composer::DisplayBrightnessOptions& options) = 0;
 71    [[nodiscard]] virtual hal::Error setActiveConfigWithConstraints(
 72            hal::HWConfigId configId, const hal::VsyncPeriodChangeConstraints& constraints,
 73            hal::VsyncPeriodChangeTimeline* outTimeline) = 0;
 74    [[nodiscard]] virtual hal::Error setBootDisplayConfig(hal::HWConfigId configId) = 0;
 75    [[nodiscard]] virtual hal::Error clearBootDisplayConfig() = 0;
 76    [[nodiscard]] virtual hal::Error getPreferredBootDisplayConfig(
 77            hal::HWConfigId* configId) const = 0;
 78    [[nodiscard]] virtual hal::Error setAutoLowLatencyMode(bool on) = 0;
 79    [[nodiscard]] virtual hal::Error getSupportedContentTypes(
 80            std::vector<hal::ContentType>*) const = 0;
 81    [[nodiscard]] virtual hal::Error setContentType(hal::ContentType) = 0;
 82    [[nodiscard]] virtual hal::Error getClientTargetProperty(
 83            aidl::android::hardware::graphics::composer3::ClientTargetPropertyWithBrightness*
 84                    outClientTargetProperty) = 0;
 85    [[nodiscard]] virtual hal::Error getDisplayDecorationSupport(
 86            std::optional<aidl::android::hardware::graphics::common::DisplayDecorationSupport>*
 87                    support) = 0;
 88    [[nodiscard]] virtual hal::Error setIdleTimerEnabled(std::chrono::milliseconds timeout) = 0;
 89    [[nodiscard]] virtual hal::Error getPhysicalDisplayOrientation(
 90            Hwc2::AidlTransform* outTransform) const = 0;
 91};
 92
 93class Layer {
 94public:
 95    virtual ~Layer();
 96
 97    virtual hal::HWLayerId getId() const = 0;
 98
 99    [[nodiscard]] virtual hal::Error setCursorPosition(int32_t x, int32_t y) = 0;
100    [[nodiscard]] virtual hal::Error setBuffer(uint32_t slot,
101                                               const android::sp<android::GraphicBuffer>& buffer,
102                                               const android::sp<android::Fence>& acquireFence) = 0;
103    [[nodiscard]] virtual hal::Error setSurfaceDamage(const android::Region& damage) = 0;
104
105    [[nodiscard]] virtual hal::Error setBlendMode(hal::BlendMode mode) = 0;
106    [[nodiscard]] virtual hal::Error setColor(
107            aidl::android::hardware::graphics::composer3::Color color) = 0;
108    [[nodiscard]] virtual hal::Error setCompositionType(
109            aidl::android::hardware::graphics::composer3::Composition type) = 0;
110    [[nodiscard]] virtual hal::Error setDataspace(hal::Dataspace dataspace) = 0;
111    [[nodiscard]] virtual hal::Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata,
112                                                         const android::HdrMetadata& metadata) = 0;
113    [[nodiscard]] virtual hal::Error setDisplayFrame(const android::Rect& frame) = 0;
114    [[nodiscard]] virtual hal::Error setPlaneAlpha(float alpha) = 0;
115    [[nodiscard]] virtual hal::Error setSidebandStream(const native_handle_t* stream) = 0;
116    [[nodiscard]] virtual hal::Error setSourceCrop(const android::FloatRect& crop) = 0;
117    [[nodiscard]] virtual hal::Error setTransform(hal::Transform transform) = 0;
118    [[nodiscard]] virtual hal::Error setVisibleRegion(const android::Region& region) = 0;
119    [[nodiscard]] virtual hal::Error setZOrder(uint32_t z) = 0;
120
121    // Composer HAL 2.3
122    [[nodiscard]] virtual hal::Error setColorTransform(const android::mat4& matrix) = 0;
123
124    // Composer HAL 2.4
125    [[nodiscard]] virtual hal::Error setLayerGenericMetadata(const std::string& name,
126                                                             bool mandatory,
127                                                             const std::vector<uint8_t>& value) = 0;
128
129    // AIDL HAL
130    [[nodiscard]] virtual hal::Error setBrightness(float brightness) = 0;
131    [[nodiscard]] virtual hal::Error setBlockingRegion(const android::Region& region) = 0;
132};
133}

举例说明

1//frameworks/native/services/surfaceflinger/DisplayHardware/HWC2.h
2[[nodiscard]] virtual hal::Error getChangedCompositionTypes(
3            std::unordered_map<Layer*, aidl::android::hardware::graphics::composer3::Composition>*
4                    outTypes) = 0;
5//frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
6error = hwcDisplay->getChangedCompositionTypes(&changedTypes);

可以发现这里的调用都是带有返回值的。

3总结

在上面的例子中,函数calculateSum被标记为[[nodiscard]],但在main函数中,它的返回值没有被使用。编译器可能会发出警告或错误,提醒开发者注意。

需要注意的是,[[nodiscard]]属性只是一种编译器提供的静态检查机制,并不能强制要求开发者一定要使用函数的返回值。但通过标记函数为[[nodiscard]],可以增加代码的可读性和可维护性,减少潜在的错误。

参考

[1] qq_38617319, nodiscard介绍 C++, 2021.