使用组合而不是继承转发方法(使用C++特征)

Jul*_*n__ 5 c++ inheritance templates metaprogramming composition

我想使用组合并使用C++功能为每个可能的重载(noexcept,const,volatile)编写好的转发方法.

我们的想法是使用traits来确定方法是否被声明{noexcept/const/volatile/etc.}并相应地表现.

这是我想要实现的一个例子:

struct User{    
    UsedObject& obj;
    User(UsedObject& obj) : obj(obj) {}

    FORWARD_METHOD(obj, get); //here is where the forwarding happens
};

struct UsedObject{
    string m{"Hello\n"};

    string& get(double d){
        cout << "\tUsed :const not called...\n";
        return m;
    }
    const string& get(double d) const{
        cout << "\tUsed :const called...\n";
        return m;
    }
};
Run Code Online (Sandbox Code Playgroud)

这是我到目前为止**:

// forward with noexcept attribute
// I'm not 100% sure about : std::declval<std::add_lvalue_reference<decltype(obj)>::type

template<typename... Args>
constexpr decltype(auto) get(Args && ... args)
noexcept(
         noexcept(std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get(  std::forward<Args>(args)...  ))
         and
         std::is_nothrow_move_constructible<decltype( std::declval<std::add_lvalue_reference<decltype(obj)>::type>().get(  std::forward<Args>(args)...  ) )>::value
         )
{
    cout << "const called...\n";
    return obj.get(std::forward<Args>(args)...);
}

// forward with noexcept and const attributes
// I'm not sure that this one behave properly.

template<typename... Args>
constexpr decltype(auto) get(Args && ... args)
const noexcept(
         noexcept(std::declval< std::add_const<decltype(obj) &>::type >().get(  std::forward<Args>(args)...  ))
         and
         std::is_nothrow_move_constructible<decltype( std::declval< std::add_const<decltype(obj) &>::type >().get(  std::forward<Args>(args)...  ) )>::value
         )
{
    cout << "const not called...\n";
    using const_type = std::add_lvalue_reference<std::add_const<std::remove_reference<decltype(obj)>::type>::type>::type;
    return const_cast<const_type>(obj).get(std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

请注意,这个问题与下面的问题不同,因为我知道我们可以使用c ++特性来检查对象接口:组合:使用特征来避免转发函数?

**受到@David Stone的评论主题的启发:我什么时候应该使用C++私有继承?.

Dav*_*one 2

我们先从解决方案开始,一点一点地解释。

#define FORWARDING_MEMBER_FUNCTION(Inner, inner, function, qualifiers) \
    template< \
        typename... Args, \
        typename return_type = decltype(std::declval<Inner qualifiers>().function(std::declval<Args &&>()...)) \
    > \
    constexpr decltype(auto) function(Args && ... args) qualifiers noexcept( \
        noexcept(std::declval<Inner qualifiers>().function(std::forward<Args>(args)...)) and \
        ( \
            std::is_reference<return_type>::value or \
            std::is_nothrow_move_constructible<return_type>::value \
        ) \
    ) { \
        return static_cast<Inner qualifiers>(inner).function(std::forward<Args>(args)...); \
    }

#define FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, reference) \
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, reference) \
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const reference) \
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, volatile reference) \
    FORWARDING_MEMBER_FUNCTION(Inner, inner, function, const volatile reference)

#define FORWARDING_MEMBER_FUNCTIONS(Inner, inner, function) \
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &) \
    FORWARDING_MEMBER_FUNCTIONS_CV(Inner, inner, function, &&)
Run Code Online (Sandbox Code Playgroud)

Inner 表示您要转发到的对象的类型,inner 表示其名称。限定符是成员函数中所需的 const、易失性、& 和 && 的组合。

noexcept 规范非常复杂,因为您需要处理函数调用以及构造返回值。如果您转发的函数返回一个引用,则您知道它是安全的(引用始终可以从同一类型构造 noexcept ),但如果该函数按值返回,则需要确保该对象的移动构造函数是 noexcept 。

我们可以通过使用默认模板参数 return_type 来稍微简化这一点,否则我们将不得不将该返回类型拼写两次。

我们在函数体中使用 static_cast 来正确添加 cv 和引用限定符到所包含的类型。函数上的引用限定符不会自动获取此值。

使用继承而不是组合

使用私有继承,解决方案看起来更像是这样:

struct Outer : private Inner {
    using Inner::f;
};
Run Code Online (Sandbox Code Playgroud)

这样做的好处是

  • 可读性
  • 更快的编译时间
  • 调试构建中的代码速度更快(无需内联)
  • 没有用完你的 constexpr 递归深度
  • 没有用完模板实例化深度
  • 按值返回不可移动类型
  • 处理转发给构造函数