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

时间:2015-02-08 11:03:43

标签: c++ templates inheritance metaprogramming composition

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

我们的想法是使用特征来确定方法是否被声明{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;
    }
};

这是我到目前为止所拥有的**:

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

请注意,这个问题与下面的问题不同,因为我知道我们可以使用c ++特征来检查对象接口:Composition: using traits to avoid forwarding functions?

**灵感来自@David Stone的评论主题:When should I use C++ private inheritance?

1 个答案:

答案 0 :(得分:1)

让我们从解决方案开始,逐一解释。

#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表示要转发的对象的类型,inner表示其名称。限定符是const,volatile,&amp;和&amp;&amp; amp;的组合。您需要的成员函数。

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

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

我们在函数体中使用static_cast来处理正确添加cv和引用限定符到包含的类型。这个函数不会通过参考限定符自动获取。

使用继承代替合成

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

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

这具有

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