Отличная пересылка функции-члена элемента данных?

Рассмотрим класс, в котором есть элемент данных 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 templates/forward...) для передачи данной функции от _data до MyClass (например, insert здесь) и предоставления того же интерфейса для пользователя?

Ответ 1

Правильный синтаксис таков:

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

Однако для этого вам не нужен С++ 14. Вы можете просто использовать синтаксис С++ 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)...);
        }
};

Ответ 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)...);
}