【问题标题】:Default parameters for polymorphic lambda多态 lambda 的默认参数
【发布时间】:2015-05-22 13:11:03
【问题描述】:

我正在尝试编写一个宏,当只需要单个 return 语句时,它会缩短 lambda 表达式语法。我的第一次尝试是这样的:

struct void_t{};                                                                                                                                     

#define LR(EXPR) (auto&& p_1 = void_t{}, auto&& p_2 = void_t{}, auto&& p_3 = void_t{}){ return EXPR; }

不幸的是,它没有像我预期的那样工作。默认参数基本上被忽略,我不能用少于三个参数调用这个 lambda:

std::cout << []LR(p_1)("test"); // compile error                                                                                                     
std::cout << []LR(p_1 + p_2)(2, 3); // compile error                                                                                                 
std::cout << []LR(p_1 + p_2)(std::string("hello "), std::string(" world!")); // compile error                                                        
std::cout << []LR(p_1 + p_2)(2, 3, 0); // OK                                                                          

我设法设计了一些看似可行但存在问题的复杂解决方法:

template<class LT> struct lambda_wrapper                                        
{                                                                               
    lambda_wrapper(LT p_lambda): m_lambda(p_lambda){}                           

    template<class T1, class T2, class T3>                                      
    auto operator()(T1&& p_1, T2&& p_2, T3&& p_3) const                         
    {                                                                           
        return m_lambda(std::forward<T1>(p_1),                                  
                        std::forward<T2>(p_2),                                  
                        std::forward<T3>(p_3));                                 
    }                                                                           

    template<class T1, class T2> auto operator()(T1&& p_1, T2&& p_2) const      
    {                                                                           
        return m_lambda(std::forward<T1>(p_1), std::forward<T2>(p_2), void_t{});
    }                                                                           

    template<class T1> auto operator()(T1&& p_1) const                          
    {                                                                           
        return m_lambda(std::forward<T1>(p_1), void_t{}, void_t{});             
    }                                                                           

    auto operator()() const                                                     
    {                                                                           
        return m_lambda(void_t{}, void_t{}, void_t{});                          
    }                                                                           
private:                                                                        
    LT m_lambda;                                                                
};                                                                              

template <class LT> lambda_wrapper<LT> operator++(LT&& p_lambda, int)           
{                                                                               
    return {std::forward<LT>(p_lambda)};                                        
}                                                                               

#define LR(EXPR) (auto&& p_1, auto&& p_2, auto&& p_3){ return EXPR; }++         

谁有更好的主意?

【问题讨论】:

  • 您可以使用返回std::function 的函数模板。或者您可以使用可变参数宏(您可以让它计算其参数并调用特定于该参数数量的宏)。
  • 1) 我不确定这个模板函数会是什么样子? 2)我的宏只有一个参数
  • 不知道您的lamda_wrapper 与您的问题有什么关系。您的 LR 宏看起来和以前一样。
  • lambda_wrapper 使表达式[]LR(p_1)("test"); 进行编译。你可能会忽略我使用的后期增量技巧。
  • 你可以使用 LR1, LR2, LR3, ... 它仍然会缩短行数,不像单个 LR 宏那样优雅,但它可以工作。

标签: c++ c++11 lambda c++14


【解决方案1】:

根据 Daniel 的回答,我们可以将元组索引编码为 std::integral_constant 类型,并使用 operator[] 模板编写 std::tuple 的扩展,该模板可以从其参数的类型推断索引:

namespace detail {
    template <typename... Ts> struct tuple : std::tuple<Ts...> {
        using std::tuple<Ts...>::tuple;
        template <typename T> constexpr auto operator[](T)
        -> std::tuple_element_t<T::value, std::tuple<Ts...>>
        { return std::get<T::value>(*this); }
    };
    template <typename... Ts> constexpr tuple<Ts&&...> forward_as_tuple(Ts&&... vs) {
        return tuple<Ts&&...>{ std::forward<Ts>(vs)... };
    }
    namespace placeholders {
        constexpr std::integral_constant<size_t, 0> _0;
        constexpr std::integral_constant<size_t, 1> _1;
        constexpr std::integral_constant<size_t, 2> _2;
        //...
    }
}

#define LR(EXPR) (auto&&... _ps) \
{ using namespace detail::placeholders; auto p = detail::forward_as_tuple(_ps...); return EXPR; }

导致

std::cout << []LR(p[_0])("test");
std::cout << []LR(p[_0] + p[_1])(2, 3); 
std::cout << []LR(p[_0] + p[_1])(std::string("hello "), std::string(" world!"));

Live example

更新:正如 Daniel 在评论中提到的,我们可以为每个参数数量专门化我们的元组类型,并为元组元素指定别名:

namespace detail {
    template <typename... Ts> struct tuple : std::tuple<Ts...> {
        using std::tuple<Ts...>::tuple;
    };
    template <typename T0> struct tuple<T0> : std::tuple<T0> {
        using std::tuple<T0>::tuple;
        T0&& _0 = std::get<0>(*this);
    };
    template <typename T0, typename T1> struct tuple<T0, T1> : std::tuple<T0, T1> {
        using std::tuple<T0, T1>::tuple;
        T0&& _0 = std::get<0>(*this);
        T1&& _1 = std::get<1>(*this);
    };
    template <typename T0, typename T1, typename T2> struct tuple<T0, T1, T2> : std::tuple<T0, T1, T2> {
        using std::tuple<T0, T1, T2>::tuple;
        T0&& _0 = std::get<0>(*this);
        T1&& _1 = std::get<1>(*this);
        T2&& _2 = std::get<2>(*this);
    };
    //...
    template <typename... Ts> constexpr tuple<Ts&&...> forward_as_tuple(Ts&&... vs) {
        return tuple<Ts&&...>{ std::forward<Ts>(vs)... };
    }
}

#define LR(EXPR) (auto&&... _ps) \
{ auto p = detail::forward_as_tuple(_ps...); return EXPR; }

因此:

std::cout << []LR(p._0)("test");
std::cout << []LR(p._0 + p._1)(2, 3); 
std::cout << []LR(p._0 + p._1)(std::string("hello "), std::string(" world!"));

Live example

【讨论】:

    【解决方案2】:

    更通用,但从表达式中访问元素的语法不太方便:

    #define LR(EXPR) (auto&&... ps) \
    { auto p = std::forward_as_tuple(ps...); return EXPR; }
    

    导致

    std::cout << []LR(std::get<0>(p))("test");
    std::cout << []LR(std::get<0>(p) + std::get<1>(p))(2, 3); 
    std::cout << []LR(std::get<0>(p) + std::get<1>(p))(std::string("hello "), std::string(" world!"));
    

    Live example


    更新:在@Oktalist 的回答 (+1) 的基础上,还可以使用标准提供的占位符:

    #include <iostream>
    #include <tuple>
    #include <functional>
    
    namespace detail
    {
    template<typename T>
    struct tuple_with_placeholder : T
    {
        using T::T;
    
        template<typename PH>
        auto operator[](PH) const
        {
            return std::get<std::is_placeholder<PH>::value-1>(*this);
        }
    };
    }
    
    #define LR(EXPR) (auto&&... ps) { \
        detail::tuple_with_placeholder<decltype(std::forward_as_tuple(ps...))> p(ps...); \
        using namespace std::placeholders; \
        return EXPR; \
    }
    
    int main()
    {
        std::cout << []LR(p[_1])("test");
        std::cout << []LR(p[_1]+p[_2])(2, 3); 
        std::cout << []LR(p[_1]+p[_2])(std::string("hello "), std::string(" world!"));
    }
    

    Live example

    【讨论】:

    • 这当然是很有前途的方法。您认为可以缩短对[]LR(p[0] + p[1]) 之类的参数访问吗?毕竟,我的问题主要是关于减少语法负担。
    • @Yahoo p[0] 是运行时的,所以这不起作用,因为每个运行时索引值的返回类型可能没有不同。 p.template get&lt;N&gt;() 是可能的,而不是改进。还有p._0p._1 可能是可以的,但是需要对每个_N 进行专业化,所以会受到限制(如果你有兴趣我可以稍后尝试,现在必须去)。遗憾的是,C++ 语法不允许满足您的需要。哎呀,我什至希望我可以通过索引直接访问参数包,例如(auto&amp;&amp;... p) { p[0] + p[1] }(是的,这可能永远不可能,但人们仍然可以梦想......)。
    • 是的,我会使用标准占位符,但事实上它们从 1 开始计数。
    【解决方案3】:

    Boost.Preprocessor 基于solution:

    #include <boost/preprocessor/tuple/to_seq.hpp>
    #include <boost/preprocessor/seq/for_each.hpp>
    #include <boost/preprocessor/seq/cat.hpp>
    #include <boost/preprocessor/seq/to_tuple.hpp>
    
    #define _AUTO(_0, _1, name) \
        (auto&& name)
    
    #define _BODY(...) \
        { return __VA_ARGS__; }
    
    #define LR(...) \
        BOOST_PP_SEQ_TO_TUPLE(BOOST_PP_SEQ_FOR_EACH(_AUTO, _, BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__)))) _BODY
    
    #include <iostream>
    
    int main()
    {
        std::cout << []LR(x)(x)("test");                                                                                                   
        std::cout << []LR(x, y)(x + y)(2, 3);                                                                                              
        std::cout << []LR(x, y)(x + y)(std::string("hello "), std::string(" world!"));                                                          
    }
    

    【讨论】:

      【解决方案4】:

      根据您的贡献,我提出了以下实现:

      namespace detail
      {
      
      struct void_t{};
      template<int I> struct int_t{};
      
      template<class TT> void_t get_param(void_t, const TT&)
      { 
          return void_t{};
      }
      
      template<int I, class TT> auto get_param(int_t<I>, const TT& p_tuple)
      -> std::tuple_element_t<I, TT>&
      { 
          return std::get<I>(p_tuple);
      }
      
      }
      
      #define LR(EXPR) (auto&&... ps) \
      {\
          auto p = std::forward_as_tuple(ps...);\
          auto&& p_1 = get_param(std::conditional_t<sizeof...(ps) >= 1,\
                                                    detail::int_t<0>,\
                                                    detail::void_t>{}, p);\
          auto&& p_2 = get_param(std::conditional_t<sizeof...(ps) >= 2,\
                                                    detail::int_t<1>,\
                                                    detail::void_t>{}, p);\
          auto&& p_3 = get_param(std::conditional_t<sizeof...(ps) >= 3,\
                                                    detail::int_t<2>,\
                                                    detail::void_t>{}, p);\
          return EXPR;\
      }
      
      int main()
      {
          std::cout << []LR(p_1)("test ", "test2");
          std::cout << []LR(p_1 + p_2)(std::string("Hallo "), std::string("World! "));
          int l_1 = 4;
          std::cout << [=]LR(p_1 + p_2 + p_3 + l_1)(1, 2, 3);
          std::cout << []LR(std::get<3>(p))(1, 2, 3, 4);    
      }
      

      谁能看出这有什么问题?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多