Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用std::invoke_result_t和泛型lambda时出现硬错误_C++_C++17_Sfinae_Generic Lambda_C++20 - Fatal编程技术网

C++ 使用std::invoke_result_t和泛型lambda时出现硬错误

C++ 使用std::invoke_result_t和泛型lambda时出现硬错误,c++,c++17,sfinae,generic-lambda,c++20,C++,C++17,Sfinae,Generic Lambda,C++20,我有一个类似容器的类,其方法类似于std::apply。我想用一个const限定符重载这个方法,但是当我试图用一个通用lambda调用这个方法时,我从std::invoke\u result\t的实例化中得到一个硬错误。我使用std::invoke_result_t来推断方法的返回值,并对参数执行SFINAE检查 #include <type_traits> #include <utility> template <typename T> class Con

我有一个类似容器的类,其方法类似于
std::apply
。我想用一个
const
限定符重载这个方法,但是当我试图用一个通用lambda调用这个方法时,我从
std::invoke\u result\t
的实例化中得到一个硬错误。我使用
std::invoke_result_t
来推断方法的返回值,并对参数执行SFINAE检查

#include <type_traits>
#include <utility>

template <typename T>
class Container
{
public:
    template <typename F>
    std::invoke_result_t<F, T &> apply(F &&f)
    {
        T dummyValue;
        return std::forward<F>(f)(dummyValue);
    }

    template <typename F>
    std::invoke_result_t<F, const T &> apply(F &&f) const
    {
        const T dummyValue;
        return std::forward<F>(f)(dummyValue);
    }
};

int main()
{
    Container<int> c;
    c.apply([](auto &&value) {
        ++value;
    });
    return 0;
}
得到了一个类似的错误:

main.cc:27:9: error: cannot assign to variable 'value' with const-qualified type 'const int &&'
        ++value;
        ^ ~~~~~
main.cc:16:41: note: in instantiation of function template specialization 'main()::(anonymous class)::operator()<const int &>' requested here
    auto apply(F &&f) const -> decltype(std::declval<F>()(std::declval<const T &>()))
                                        ^
main.cc:26:7: note: while substituting deduced template arguments into function template 'apply' [with F = (lambda at main.cc:26:13)]
    c.apply([](auto &&value) {
      ^
main.cc:26:23: note: variable 'value' declared const here
    c.apply([](auto &&value) {
               ~~~~~~~^~~~~
main.cc:27:9:错误:无法为常量限定类型为“const int&&”的变量“value”赋值
++价值观;
^ ~~~~~
main.cc:16:41:注意:在函数模板专门化的实例化中,此处请求了“main()::(匿名类)::operator()
自动应用(F&&F)const->decltype(std::declval()(std::declval())
^
main.cc:26:7:注意:在将推导出的模板参数替换为函数模板'apply'时[使用F=(main.cc:26:13处的lambda)]
c、 应用([](自动和值){
^
main.cc:26:23:注意:变量'value'在这里声明为const
c、 应用([](自动和值){
~~~~~~~^~~~~
问题:

  • 为什么会发生这种情况?更准确地说,为什么lambda的身体在重载解析过程中被实例化
  • 我如何解决这个问题

  • lambda已推导出返回类型,除非您明确指定返回类型。因此,
    std::invoke_result_t
    必须实例化主体以确定返回类型。此实例化不在直接上下文中,并导致硬错误

    您可以通过编写以下命令来编译代码:

    [](auto &&value) -> void { /* ... */ }
    

    在这里,lambda的主体在
    apply
    的主体出现之前不会被实例化,并且您处于清除状态。

    因此重载解析在这里有点愚蠢

    它并没有说“好吧,如果非
    const
    apply
    有效,我永远不会调用
    const-apply
    ,所以我不会费心考虑它”

    相反,重载解析会评估每一个可能的候选者。然后,它会消除那些遭受替换失败的候选者。只有这样,它才会对候选者排序并选择一个

    因此,这两种方法都将
    F
    替换为:

    template <typename F>
    std::invoke_result_t<F, T &> apply(F &&f)
    
    template <typename F>
    std::invoke_result_t<F, const T &> apply(F &&f) const
    
    执行此操作后,
    的两个重载都将应用

    template <typename F>
    std::invoke_result_t<F, T &> apply(F &&f)
    
    template <typename F>
    std::invoke_result_t<F, const T &> apply(F &&f) const
    
    现在,请注意,
    apply const
    仍然存在。如果调用
    apply const
    ,您将得到实例化lambda主体所导致的硬错误

    如果希望lambda本身对SFINAE友好,则需要执行以下操作:

    #define RETURNS(...) \
      noexcept(noexcept(__VA_ARGS__)) \
      -> decltype(__VA_ARGS__) \
      { return __VA_ARGS__; }
    
    [](auto &&value) RETURNS(++value)
    
    请注意,此lambda略有不同,因为它返回对值的引用。我们可以通过以下方法避免此情况:

    [](auto &&value) RETURNS((void)++value)
    
    现在lambda是SFINAE友好的,并且与原始lambda具有相同的行为,并且原始程序按照此更改进行编译

    这样做的副作用是,SFINAE现在从重载解析中消除了非constapply,从而使其对SFINAE友好


    有人建议使用
    RETURNS
    并将其重命名为
    =>
    ,但上次我检查时,它没有被接受。

    如果你这样做,它将被一个
    const
    容器调用。@Yakk AdamNevraumont这只是更改了硬错误发生的位置。非
    const
    apply不能被调用是的。@Brian,感谢您解释为什么lambda主体实例化是必要的。感谢您指出,通用lambda本身需要进行一些SFINAE检查,因为它本质上是一个模板方法的定义。
    template <typename F>
    std::invoke_result_t<F, T &> apply(F &&f)
    
    template <typename F>
    std::invoke_result_t<F, const T &> apply(F &&f) const
    
    template <typename F=$lambda$>
    void apply(F &&f)
    
    template <typename F=$lambda$>
    void apply(F &&f) const
    
    #define RETURNS(...) \
      noexcept(noexcept(__VA_ARGS__)) \
      -> decltype(__VA_ARGS__) \
      { return __VA_ARGS__; }
    
    [](auto &&value) RETURNS(++value)
    
    [](auto &&value) RETURNS((void)++value)