简体   繁体   English

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

[英]Method forwarding with composition instead of inheritance (using C++ traits)

I would like to use composition and to write good forwarding methods for every possible overload (noexcept, const, volatile) using C++ capabilities. 我想使用组合并使用C ++功能为每个可能的重载(noexcept,const,volatile)编写好的转发方法。

The idea is to use traits in order to determine whether a method is declared {noexcept / const / volatile / etc.} and to behave accordingly. 我们的想法是使用traits来确定方法是否被声明{noexcept / const / volatile / etc.}并相应地表现。

Here is an example of what I would like to achieve : 这是我想要实现的一个例子:

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;
    }
};

Here is what I have so far** : 这是我到目前为止**:

// 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)...);
}

Please note that this question is different from the following one, because I know that we can use c++ traits in order to inspect an object interface : Composition: using traits to avoid forwarding functions? 请注意,这个问题与下面的问题不同,因为我知道我们可以使用c ++特性来检查对象接口: 组合:使用特征来避免转发函数?

** inspired by a thread of comments with @David Stone here : When should I use C++ private inheritance? **受到@David Stone的评论主题的启发: 我什么时候应该使用C ++私有继承? .

Let's start with the solution and explain it piece by piece. 让我们从解决方案开始,逐一解释。

#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, &&)

Inner represents the type of the object you are forwarding to, and inner represents its name. Inner表示要转发的对象的类型,inner表示其名称。 Qualifiers is the combination of const, volatile, &, and && that you need on your member function. 限定符是您在成员函数上需要的const,volatile,&和&&的组合。

The noexcept specification is surprisingly complicated just because you need to handle the function call as well as constructing the return value. noexcept规范非常复杂,因为您需要处理函数调用以及构造返回值。 If the function you are forwarding returns a reference, you know it is safe (references are always noexcept constructible from the same type), but if the function returned by value, you need to make sure that object's move constructor is noexcept. 如果你要转发的函数返回一个引用,你知道它是安全的(引用总是noexcept可以从同一类型构造),但如果函数返回值,你需要确保对象的移动构造函数是noexcept。

We were able to simplify this a little bit by using a defaulted template argument return_type, otherwise we would have had to spell out that return type twice. 我们能够通过使用默认模板参数return_type来简化这一点,否则我们将不得不拼写两次返回类型。

We use the static_cast in the body of the function to handle properly adding cv and reference qualifiers to the contained type. 我们使用函数体中的static_cast来处理正确添加cv和引用限定符到包含的类型。 This is not automatically picked up by reference qualifiers on the function. 函数上的参考限定符不会自动获取。

Using inheritance instead of composition 使用继承而不是组合

Using private inheritance, the solution looks more like this: 使用私有继承,解决方案看起来更像这样:

struct Outer : private Inner {
    using Inner::f;
};

This has the advantage of 这具有优势

  • Readability 可读性
  • Faster compile times 编译时间更快
  • Faster code in debug builds (nothing to inline) 调试版本中的代码更快(无需内联)
  • Not using up your constexpr recursion depth 没有耗尽你的constexpr递归深度
  • Not using up your template instantiation depth 没有用完你的模板实例化深度
  • Working with returning non-movable types by value 使用值返回不可移动类型
  • Working with forwarding to constructors 使用转发到构造函数

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM