我有一个包装任何类型的类,只允许通过特定函数对该类型进行操作。这有助于我需要控制流量,同步等的许多情况。
所以我有一个名为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
?
答案 0 :(得分:1)
我合并了here和here给出的特征,以确定提供的可调用对象是否有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;
}