【问题标题】:Template parameter dependant [[nodiscard]]模板参数依赖 [[nodiscard]]
【发布时间】:2019-10-26 16:58:36
【问题描述】:

我有一个函数对象,它是另一个函数的包装器:

template <typename FuncT>
class Wrapper
{
    private:
        FuncT funcToWrap;

    public:
        Wrapper(FuncT ftw) : funcToWrap(ftw){};

        template<typename ...ARG>
        typename std::result_of<FuncT(ARG&&...)>::type operator()(ARG&&... args){
            return funcToWrap(std::forward<ARG>(args)...);
        }
};

int main(){
    std::function<void()> testfunc = [](){ std::cout << "Test" << std::endl; };
    Wrapper<decltype(testfunc)> test{testfunc};
    test();
}

如果std::result_of&lt;FuncT(ARG&amp;&amp;...)&gt;::type 不是void,我想做的是将operator() 标记为[[nodiscard]]。

我注意到的是,当我将 [[nodiscard]] 用于返回类型的模板评估到 void 时,它只会被我的编译器忽略。

这是我可以依赖的行为吗,是否以任何方式标准化?

【问题讨论】:

  • 这显然是not guaranteed by the standard,但我没有找到来自 gcc 或 clang 的关于 void nodiscard 的文档 :(
  • std::result_of 是 deprecated in C++17 and removed in C++20。请改用std::invoke_result。
  • 标准似乎未指定。但老实说,我只是把属性放在那里而不担心。属性是对编译器的非绑定提示。只要它们在语法上是正确的,它们就不会将原本格式良好的程序变成格式错误的程序。
  • 我想知道是否应该将其报告为文档错误...
  • 解决缺乏保证的问题:wandbox.org/permlink/xAQvlhtB6CXU8YnV

标签: c++ attributes c++17 nodiscard


【解决方案1】:

您可以使用 SFINAE 在两个重载的 operator () 之间进行选择:其中一个返回 void,另一个用于使用 [[nodiscard]] 属性注释的其余情况:

#include <type_traits>
#include <iostream>

template <typename FuncT>
class Wrapper
{
    private:
        FuncT funcToWrap;

    public:
        Wrapper(FuncT ftw) : funcToWrap(ftw) {}

        template <typename ...ARG, typename T = std::invoke_result_t<FuncT, ARG...>>
        std::enable_if_t<std::is_void_v<T>> operator()(ARG&&... args) {
            std::cout << "Test 1" << std::endl;
            return funcToWrap(std::forward<ARG>(args)...);
        }

        template <typename ...ARG, typename T = std::invoke_result_t<FuncT, ARG...>>
        [[nodiscard]] std::enable_if_t<!std::is_void_v<T>, T> operator()(ARG&&... args) {
            std::cout << "Test 2" << std::endl;
            return funcToWrap(std::forward<ARG>(args)...);
        }
};

int main() {
    auto testfunc1 = [] { };
    Wrapper test1{testfunc1};
    test1(); // <-- No warnings should be issued here

    auto testfunc2 = [] { return 0; };
    Wrapper test2{testfunc2};
    test2(); // <-- Warning issued here
}

【讨论】:

  • 问题的重点是我不想做上述事情。这是我开始的,我想知道我是否必须这样做,或者在这种情况下是否可以放弃 sfinea 并使用 [[nodiscard]] void。
  • @cerkiewny 问题的标题和“我想做什么......”这句话不建议这样做。无论如何,我发布了答案,因为我被要求。如果您认为它对其他用户没有用,我可以删除它。
  • 它可以留下来,如果有人因为怀疑如何处理问题而到达那里,它是有用的,但是我只是标记这不能回答我的担忧,但它肯定是相关的
【解决方案2】:

每[dcl.attr.nodiscard]/2:

[ 注意: nodiscard 调用是一个函数调用表达式,它调用一个 之前声明的函数nodiscard,或者它的返回类型是 可能标记为 nodiscard 的 cv 限定类或枚举类型。 nodiscard 呼叫作为潜在评估的外观 不鼓励丢弃值表达式,除非显式转换为 void。在这种情况下,实现应该发出警告。这是 通常是因为丢弃 nodiscard 调用的返回值 令人惊讶的后果。 — 尾注 ]

我对这一段的阅读给出了这一点

[[nodiscard]] void f() {}

甚至

f();

应该发出警告。您必须明确转换为void,如

(void) f();

压制它。所以不,标准不保证这一点。

在我看来,标准只是忽略了这种微妙之处。

【讨论】:

    猜你喜欢
    • 2011-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-07
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 2023-01-24
    相关资源
    最近更新 更多