代码之家  ›  专栏  ›  技术社区  ›  Vittorio Romeo

在泛型lambda中使用“if constexpr”访问成员类型需要两个分支都是格式良好的-gcc与clang

  •  19
  • Vittorio Romeo  · 技术社区  · 7 年前

    struct

    struct foo { using x = int;   };
    struct bar { using y = float; };
    

    给定 T 在一个 template T::x T::y T 是:

    template <typename T>
    auto s()
    {
        auto l = [](auto p) 
        {
            if constexpr(p) { return typename T::x{}; }
            else            { return typename T::y{}; }
        };
    
        return l(std::is_same<T, foo>{});
    }
    
    int main() 
    { 
        s<foo>(); 
    }
    

    g++ clang++ 产生此错误:

    error: no type named 'y' in 'foo'
            else            { return typename T::y{}; }
                                     ~~~~~~~~~~~~^
    note: in instantiation of function template specialization 's<foo>' requested here
        s<foo>();
        ^
    

    on godbolt.org , with conformance viewer


    叮当声++ 错误地拒绝此代码?

    叮当声++ l :

    template <typename T>
    auto s()
    {
        if constexpr(std::is_same<T, foo>{}) { return typename T::x{}; }
        else                                 { return typename T::y{}; }
    }
    
    1 回复  |  直到 7 年前
        1
  •  6
  •   Columbo    7 年前

    看见 Richard Smith's post

    在我熟悉的实现中(即Clang),一个关键问题是处理函数定义时使用的词法范围基本上是瞬态的,这意味着很难支持延迟实例化函数模板定义的某些部分。泛型lambda在这里没有问题,因为泛型lamdda的主体是用封闭函数模板[…]实例化的

    也就是说,在实例化模板时,使用局部上下文(包括模板参数)部分实例化泛型lambda的主体;因此,在克朗的实施下, T::x T::y 直接替换,因为闭包类型可以传递到外部。这导致了失败。正如@T.C.所指出的,该代码可以被视为格式不良,不需要诊断,因为它是 s<foo> 生成一个模板定义(闭包的定义),其第二个 if constexpr

    这归结为主要实现中的架构问题(另请参见 this answer ; GCC显然不受此限制),因此如果Core认为您的代码格式良好,我会感到惊讶(毕竟,他们在通用lambda捕获的设计中考虑了这一点——请参阅链接的答案)。支持代码的GCC充其量是一个特性(但可能有害,因为它使您能够编写依赖于实现的代码)。