7

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

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();
}

我想做的是将 标记operator()[[nodiscard]]好像std::result_of<FuncT(ARG&&...)>::type不是void

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

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

4

2 回答 2

2

您可以使用 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
}
于 2019-09-25T06:33:55.840 回答
1

根据[dcl.attr.nodiscard]/2

[ <em>注意:nodiscard 调用是调用先前声明的函数的函数调用表达式nodiscard,或者其返回类型可能是 cv 限定的类或标记的枚举类型nodiscard。不鼓励将 nodiscard 调用作为潜在评估的丢弃值表达式出现,除非显式转换为 void. 在这种情况下,实现应该发出警告。这通常是因为丢弃 nodiscard 调用的返回值会产生令人惊讶的后果。— <em>结束注释]

我对这一段的阅读表明,鉴于

[[nodiscard]] void f() {}

甚至

f();

应该发出警告。你必须明确地转换void

(void) f();

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

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

于 2019-06-12T10:45:20.667 回答