【问题标题】:Storing compile-time values of arbitrary type in a generic lambda在通用 lambda 中存储任意类型的编译时值
【发布时间】:2021-12-07 15:13:15
【问题描述】:

以下 C++-20 代码允许滥用 lambda 来存储编译时值:

#include <type_traits>
#include <iostream>

consteval auto addItem(auto origin, auto key, auto value) 
    requires (std::is_same_v<void, decltype(origin(key))>) {
    
    auto result = [=](auto arg) constexpr
    {
        if constexpr(std::is_same_v<decltype(arg), decltype(key)>) {
            return value;
        } 
        else if constexpr(!std::is_same_v<void, decltype(origin(arg))>)
        {
            return origin(arg);    
        }
    };

    return result;
}

template<int I> struct index {};

int main()
{
    constexpr auto c = [](auto) {};
    constexpr auto c_ = addItem(c, index<1>{}, "Hello");
    constexpr auto c__ = addItem(c_, index<2>{}, 42);  
    
    // Will fail to compile as expected, since requires clause above kicks in
    // constexpr auto c___ = addItem(c__, index<2>{}, "WAT");  

    std::cout << c__(index<1>{}) << " ";
    std::cout << c__(index<2>{}) << "\n";   
    
    // Will fail to compile as expected, since return type is void 
    // This is the point where I wish I had a more expressive error message
    // std::cout << c__(index<3>{}) << "\n";   
}

这很好,但是公共接口有点笨拙,所以我想直接使用索引值来解决这个问题:

#include <type_traits>
#include <iostream>

template<int I>
consteval auto addItem(auto origin, auto value) 
    requires (std::is_same_v<void, decltype(origin(I))>) {
    
    auto result = [=]<int J>() constexpr
    {
        if constexpr(I == J) {
            return value;
        } 
        else 
        {
            // Fails to compile  
            return origin<J>();    
        }
    };

    return result;
}

int main()
{
    constexpr auto c = []<int I>() {};

    constexpr auto c_ = addItem<1>(c, "Hello");
    constexpr auto c__ = addItem<2>(c_, 42);  
    
    std::cout << c__<1>() << " ";
    std::cout << c__<2>() << "\n";   
}

此尝试失败并显示错误消息

Output of x86-64 clang (trunk) (Compiler #1)
<source>:15:20: error: 'origin' does not name a template but is followed by template arguments
            return origin<J>();    
                   ^     ~~~

看起来 auto 不像我希望的那样自动。在不更改公共界面的情况下,您知道如何实现我的目标吗?

【问题讨论】:

  • 这是不可能的,因为当您编写constexpr auto c = ... 时,您永远不会获得可以直接向其提供模板参数的实体,如c&lt;I&gt;。你总是得到一个具体的对象(当然,它可能有模板化的成员)。
  • 由于c.template operator()&lt;2&gt;(); 是有效代码,我倾向于不同意..

标签: c++ templates lambda


【解决方案1】:

正如 Brian 指出的那样,直接调用 c&lt;1&gt;() 是不可能的,但是通过额外的语法代码,编译器终于可以推断出它:

#include <type_traits>
#include <iostream>

template<int I>
consteval auto addItem(auto origin, auto value) 
    requires (std::is_same_v<void, decltype(origin.template operator()<I>())>) {
    
    auto result = [=]<int J>() constexpr
    {
        if constexpr(I == J) {
            return value;
        } 
        else 
        {
            return origin.template operator()<J>();    
        }
    };

    return result;
}

template<int I>
consteval auto get(auto c) {
    return c.template operator()<I>();
}


int main()
{
    constexpr auto c = []<int I>() {};
    c.template operator()<2>();

    constexpr auto c_ = addItem<1>(c, "Hello");
    constexpr auto c__ = addItem<2>(c_, 42);  
    
    std::cout << get<1>(c__) << " ";
    std::cout << get<2>(c__) << "\n";   
}

不完全是要求的解决方案,但额外的间接级别隐藏了语法的丑陋。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多