将数据成员的成员函数进行完美转发?

3

考虑一个拥有 private std::vector 成员变量的类:

class MyClass 
{
    private: 
        std::vector<double> _data;
    public:
        template <class... Args>
        /* something */ insert(Args&&... args) /* something */
        {
            return _data.insert(std::forward<Args>(args)...);
        }
};

使用C++14的auto/variadic模板/forwarding,将给定的函数_data传递到MyClass中(例如此处的insert),并为用户提供相同的接口,正确的语法是什么?

你是指insert包装器的返回类型吗?decltype(auto) - dyp
参数列表后的 something 应包含正确的 cv 和 ref 限定符以及异常说明吗? - dyp
1
“将给定的 _data 函数转移到 MyClass 的正确语法是什么?” -> 嗯,什么? - fredoverflow
2个回答

6
正确的语法是这样的:
class MyClass 
{
    private: 
        std::vector<double> _data;
    public:
        template <class... Args>
        decltype(auto) insert(Args&&... args)
        {
            return _data.insert(std::forward<Args>(args)...);
        }
};

然而,实际上你不需要用C++14来做到这一点,你可以使用C++11的语法。

class MyClass 
{
    private: 
        std::vector<double> _data;
    public:
        template <class... Args>
        auto insert(Args&&... args) 
        -> decltype(_data.insert(std::forward<Args>(args)...))
        {
            return _data.insert(std::forward<Args>(args)...);
        }
};

或者使用常规的C++:std::vector::iterator insert(...) - David G
我认为 OP 希望包装函数也具有相同的异常规范和 cv 限定符(以及可能的 ref 限定符,尽管这对于 std::vector 不相关)。 - Jonathan Wakely
3
也许有一天我们会拥有noexcept(auto) :P,这句话的意思是可能在将来的某个时候,编程语言会支持类似于noexcept(auto)这样的语法。 - Rapptz

2
为了真正地将调用转发到成员函数,我们必须考虑正确转发成员调用的*this值的必要性。
下面是示例:
template<typename Type>
struct Fwd {
    Type member;

    template<typename ...Args>
    decltype(auto) Func(Args&&... args)
        noexcept(noexcept(member.Func(std::forward<Args>(args)...)))
        { return member.Func(std::forward<Args>(args)...); }
};

正如你可能猜到的那样,它足以转发参数和异常规范。 但是,它不足以完美转发*this

struct S {
    // These overloads are reachable through Fwd<S>::Func().
    void Func(int) & {}
    void Func(int&&) & {}

    // These other overloads are not.
    void Func(int) const&;
    void Func(int&&) const&;
    void Func(int) volatile&;
    void Func(int&&) volatile&;
    void Func(int) const volatile&;
    void Func(int&&) const volatile&;
    void Func(int) &&;
    void Func(int&&) &&;
    // (These are rather uncommon, just provided for completude.)
    void Func(int) const&&;
    void Func(int&&) const&&;
    void Func(int) volatile&&;
    void Func(int&&) volatile&&;
    void Func(int) const volatile&&;
    void Func(int&&) const volatile&&;
};

针对这个问题,有两种解决方案。第一种是手动创建所有可能的重载函数,如果需要的话可以使用宏来简化操作:

#define FWD(member, Func) \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) & \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return member.Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) const& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return member.Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) volatile& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return member.Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) const volatile& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return member.Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) && \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return std::move(member).Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) const&& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return std::move(member).Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) volatile&& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return std::move(member).Func(std::forward<Args>(args)...); } \
    \
    template<typename ...Args> \
    decltype(auto) Func(Args&&... args) const volatile&& \
        noexcept(noexcept(member.Func(std::forward<Args>(args)...))) \
        { return std::move(member).Func(std::forward<Args>(args)...); }

template<typename Type>
struct Fwd {
    Type member;
    FWD(member, Func)
};

另一种解决方案是完全避免使用成员函数,而是使用自由函数:
template<typename Fwd, typename ...Args>
decltype(auto) Func(Fwd&& fwd, Args&&... args)
    noexcept(noexcept(std::forward<Fwd>(fwd).Func(std::forward<Args>(args)...))) {
    return std::forward<Fwd>(fwd).Func(std::forward<Args>(args)...);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接