C ++ 11强制模板参数通过引用传递

时间:2016-08-07 08:07:04

标签: templates c++11

我有一个包装任何类型的类,只允许通过特定函数对该类型进行操作。这有助于我需要控制流量,同步等的许多情况。

所以我有一个名为ObjectWrapper的简单模板类,它公开了1 UseMe方法,以允许使用内部对象。

例如,如果每次使用此对象时都需要打印,我可以使用以下代码:

template <typename Object>
class ObjectWrapper
{
public:
    template <typename Functor>
    typename std::result_of<Functor(Object&)>::type UseMe(const Functor& functor)
    {
        std::cout << "Someone is using me!" << std::endl;
        return functor(m_object);
    }

private:
    Object m_object;
};

我可以这样使用它:

void main()
{
    mySpecialString.UseMe([](std::string& innerObject) {
        innerObject = "My Special string";
    });

    mySpecialString.UseMe([](std::string& innerObject) {
        std::cout << innerObject << std::endl;
    });

    size_t length = mySpecialString.UseMe([](std::string& innerObject) {
        return innerObject.size();
    });

    std::cout << length << std::endl;
}

将打印:

  

有人在用我!   有人在用我!   我的特殊字符串
  有人在用我!   17

这正是我想要的,但它有一个缺陷,即开发人员可以编写以下内容:

void main()
{
    ObjectWrapper<std::string> mySpecialString;
    mySpecialString.UseMe([](std::string innerObject) {
        innerObject = "My Special string";
    });

    mySpecialString.UseMe([](std::string innerObject) {
        std::cout << innerObject << std::endl;
    });
    size_t length = mySpecialString.UseMe([](std::string innerObject) {
        return innerObject.size();
    });

    std::cout << length << std::endl;
}

将打印:

  

有人在使用我!
  有人在用我!

     

有人在使用我!
  0

[对于那些错过它的人来说,字符串是通过值传递给lambda而不是按照我的意图传递给

我认为typename std::result_of<Functor(Object&)>::typ将有助于强制通过ref传递参数,但它没有。

我也使用static_assert,但无法弄清楚如何获取传递参数的类型。

如何强制此ObjectWrapper的用户仅通过引用获取模板Object

1 个答案:

答案 0 :(得分:1)

我合并了herehere给出的特征,以确定提供的可调用对象是否有Object&作为其参数:

#include <iostream>
#include <utility>
#include <functional>


template<class> struct rm_func_cv; // undefined
template<class R, class C, class... ArgTypes>
struct rm_func_cv<R(C::*)(ArgTypes...)>  { using type = R(C::*)(ArgTypes...); };
template<class R, class C, class... ArgTypes>
struct rm_func_cv<R(C::*)(ArgTypes...) const>  { using type = R(C::*)(ArgTypes...); };
template<class R, class C, class... ArgTypes>
struct rm_func_cv<R(C::*)(ArgTypes...) volatile>  { using type = R(C::*)(ArgTypes...); };
template<class R, class C, class... ArgTypes>
struct rm_func_cv<R(C::*)(ArgTypes...) const volatile>  { using type = R(C::*)(ArgTypes...); };

namespace {
    // build R (*)(Args...) from R (Args...)
    // compile error if signature is not a valid function signature
    template <typename, typename>
    struct build_free_function;

    template <typename F, typename R, typename ... Args>
    struct build_free_function<F, R (Args...)>
    { using type = R (*)(Args...); };

    // build R (C::*)(Args...) from R (Args...)
    //       R (C::*)(Args...) const from R (Args...) const
    //       R (C::*)(Args...) volatile from R (Args...) volatile
    // compile error if signature is not a valid member function signature
    template <typename, typename>
    struct build_class_function;

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...)>
    { using type = R (C::*)(Args...); };

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...) const>
    { using type = R (C::*)(Args...) const; };

    template <typename C, typename R, typename ... Args>
    struct build_class_function<C, R (Args...) volatile>
    { using type = R (C::*)(Args...) volatile; };

    // determine whether a class C has an operator() with signature S
    template <typename C, typename S>
    struct is_functor_with_signature
    {
        static bool constexpr value  = std::is_same<typename build_class_function<C, S>::type, typename rm_func_cv<decltype(&C::operator())>::type>::value;
    };

    // determine whether a free function pointer F has signature S
    template <typename F, typename S>
    struct is_function_with_signature
    {
        // check whether F and the function pointer of S are of the same
        // type
        static bool constexpr value = std::is_same<
            F, typename build_free_function<F, S>::type
        >::value;
    };

    // C is a class, delegate to is_functor_with_signature
    template <typename C, typename S, bool>
    struct is_callable_impl
        : std::integral_constant<
            bool, is_functor_with_signature<C, S>::value
          >
    {};

    // F is not a class, delegate to is_function_with_signature
    template <typename F, typename S>
    struct is_callable_impl<F, S, false>
        : std::integral_constant<
            bool, is_function_with_signature<F, S>::value
          >
    {};
}

// Determine whether type Callable is callable with signature Signature.
// Compliant with functors, i.e. classes that declare operator(); and free
// function pointers: R (*)(Args...), but not R (Args...)!
template <typename Callable, typename Signature>
struct is_callable
    : is_callable_impl<
        Callable, Signature,
        std::is_class<Callable>::value
      >
{};

template <typename Object>
class ObjectWrapper
{
public:
    template <typename Functor>
    auto UseMe(const Functor& functor)
    {
        using R = decltype(functor(m_object));
        static_assert(is_callable<std::decay_t<Functor>, R(Object&)>::value, "signature must be Object&");
        return functor(m_object);
    }

private:
    Object m_object;
};


void fun(std::string& arg)
{
    std::cout << "fun: " << arg << std::endl;
}

int main()
{
    ObjectWrapper<std::string> mySpecialString;

    mySpecialString.UseMe(fun);


    mySpecialString.UseMe([](std::string& innerObject) {
        innerObject = "My Special string";
    });

    mySpecialString.UseMe([](std::string& innerObject) {
        std::cout << innerObject << std::endl;
    });

    size_t length = mySpecialString.UseMe([](std::string& innerObject) {
        return innerObject.size();
    });

    std::cout << length << std::endl;
}

live example