在pre-C ++ 11中,当使用特定参数调用时,如何确定给定函数是否返回引用?
例如,如果代码如下所示:
template<class F>
bool returns_reference(F f) { return is_reference(f(5)); }
那我应该如何实施is_reference
?
请注意,f
也可能是一个仿函数,其operator()
可能有多个重载 - 我只关心实际通过我的参数调用的重载。
答案 0 :(得分:2)
这是一个基于SFINAE的解决方案,用于检查函数调用表达式是否产生左值:
#include <boost/type_traits.hpp>
#include <boost/utility.hpp>
#include <cstddef>
// Func: function (object/pointer/reference) type
// Arg0: type of the first argument to use (for overload resolution)
template<class Func, class Arg0>
struct yields_lvalue_1 // with one argument
{
typedef char yes[1];
typedef char no[2];
// decay possible function types
typedef typename boost::decay<Func>::type F_decayed;
// a type whose constructor can take any lvalue expression
struct Any
{
template<class T>
Any(T&);
};
// SFINAE-test: if `Any(....)` is well-formed, this overload of `test` is
// viable
template<class T>
static yes& test(boost::integral_constant<std::size_t,
sizeof(Any( boost::declval<T>()(boost::declval<Arg0>()) ))>*);
// fall-back
template<class T>
static no& test(...);
// perform test
static bool const result = sizeof(test<F_decayed>(0)) == sizeof(yes);
};
一些示例性功能对象:
struct foo
{
bool& operator()(int);
bool operator()(double);
};
struct bar
{
template<class T>
double operator()(T);
};
用法示例:
#include <iostream>
#include <iomanip>
void print(bool expect, bool result)
{
std::cout << "expect: "<<std::setw(5)<<expect<<" -- result: "<<result<<"\n";
}
int main()
{
std::cout << std::boolalpha;
print(true , yields_lvalue_1<foo, int> ::result);
print(false, yields_lvalue_1<foo, double>::result);
print(false, yields_lvalue_1<bar, int> ::result);
print(true , yields_lvalue_1<foo&(*)(long), int>::result);
print(false, yields_lvalue_1<void(*)(int), short>::result);
print(true , yields_lvalue_1<bool&(short), long>::result);
print(false, yields_lvalue_1<void(float), int>::result);
print(true , yields_lvalue_1<char&(&)(bool), long>::result);
print(false, yields_lvalue_1<foo(&)(int), short>::result);
}
答案 1 :(得分:1)
我找到了自己问题的答案。
returns_reference
会返回尺寸为&gt;的类型如果函数的返回类型是引用,则为1。
它适用于大多数情况,但const
和volatile
的组合数量随参数数量呈指数增长。
每当它不起作用时 - 是否因为我们有多个参数或者重载决策是否相当模糊(例如,当const
operator()
版本的const
有效时, reftype
版本没有),用户应在所有参数上使用returns_reference
,然后再将其传递给reftype
。
我认为 decltype
版本在C ++ 11中可能仍然有少数边缘情况(关于实际上是l的r值引用) -lvalues),但现在对我来说“足够好”了。那时,实际上什么是“参考”的问题本身就是模棱两可的。 (虽然,如果我们使用C ++ 11,我们可以使用decltype
而忘记所有这些。我们仍然会在C ++ 11下使用它的唯一情况是编译器不支持{ {1}}但确实支持r值引用。)
template<bool B> struct static_bool { unsigned char _[B + 1]; static_bool(...) { } operator bool() const { return B; } };
static_bool<false> returns_reference(...) { return false; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T &(*)(void)>(0) ()))> returns_reference(F, T &) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T const &(*)(void)>(0) ()))> returns_reference(F, T const &) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T volatile &(*)(void)>(0) ()))> returns_reference(F, T volatile &) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T const volatile &(*)(void)>(0) ()))> returns_reference(F, T const volatile &) { return NULL; }
template<class T> struct type_wrapper { };
template<class T> type_wrapper<T &> reftype(T &) { return type_wrapper<T &>(); }
template<class T> type_wrapper<T const &> reftype(T const &) { return type_wrapper<T const &>(); }
template<class T> type_wrapper<T volatile &> reftype(T volatile &) { return type_wrapper<T volatile &>(); }
template<class T> type_wrapper<T const volatile &> reftype(T const volatile &) { return type_wrapper<T const volatile &>(); }
#if __cplusplus >= 201103L
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T &&(*)(void)>(0) ()))> returns_reference(F, T &&) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T const &&(*)(void)>(0) ()))> returns_reference(F, T const &&) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T volatile &&(*)(void)>(0) ()))> returns_reference(F, T volatile &&) { return NULL; }
template<class T, class F> static_bool<!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T const volatile &&(*)(void)>(0) ()))> returns_reference(F, T const volatile &&) { return NULL; }
template<class T> type_wrapper<T &&> reftype(T &&) { return type_wrapper<T &&>(); }
template<class T> type_wrapper<T const &&> reftype(T const &&) { return type_wrapper<T const &&>(); }
template<class T> type_wrapper<T volatile &&> reftype(T volatile &&) { return type_wrapper<T volatile &&>(); }
template<class T> type_wrapper<T const volatile &&> reftype(T const volatile &&) { return type_wrapper<T const volatile &&>(); }
#endif
template<class T, class F> static_bool<
!!sizeof(&(static_cast<F(*)()>(0) ())(static_cast<T (*)(void)>(0) ()))
> returns_reference(type_wrapper<F>, type_wrapper<T> = type_wrapper<T>()) { return NULL; }
测试代码:
struct Test
{
Test() { }
Test(Test &) { }
Test(Test const &) { }
Test(Test volatile &) { }
Test(Test const volatile &) { }
Test *operator()(Test *p) const;
Test const *operator()(Test const *p) const;
Test volatile *operator()(Test volatile *p) const;
Test const volatile *operator()(Test const volatile *p) const;
Test &operator()(Test &p) const;
Test const &operator()(Test const &p) const;
Test volatile &operator()(Test volatile &p) const;
Test const volatile &operator()(Test const volatile &p) const;
#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
Test &&operator()(Test &&p) const { return std::move(p); }
Test const &&operator()(Test const &&p) const;
Test volatile &&operator()(Test volatile &&p) const;
Test const volatile &&operator()(Test const volatile &&p) const;
#endif
};
Test *test1(Test *p);
Test const *test2(Test const *p);
Test volatile *test3(Test volatile *p);
Test const volatile *test4(Test const volatile *p);
Test &test5(Test &p);
Test const &test6(Test const &p);
Test volatile &test7(Test volatile &p);
Test const volatile &test8(Test const volatile &p);
#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
Test &&test9(Test &&p);
Test const &&test10(Test const &&p);
Test volatile &&test11(Test volatile &&p);
Test const volatile &&test12(Test const volatile &&p);
#endif
int main()
{
Test t; (void)t;
Test const tc; (void)tc;
Test volatile tv; (void)tv;
Test const volatile tcv; (void)tcv;
std::cerr << (sizeof(returns_reference( t , &t )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , &tc )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , &tv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , &tcv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , t )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , tc )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , tv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( t , tcv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test1 , &t )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test2 , &tc )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test3 , &tv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test4 , &tcv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test5 , t )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test6 , tc )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test7 , tv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test8 , tcv )) != sizeof(unsigned char)) << std::endl;
#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
std::cerr << (sizeof(returns_reference( test9 , t )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test10 , tc )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test11 , tv )) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference( test12 , tcv )) != sizeof(unsigned char)) << std::endl;
#endif
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(&t ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(&tc ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(&tv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(&tcv))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(t ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(tc ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(tv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(t ), reftype(tcv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test1 ), reftype(&t ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test2 ), reftype(&tc ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test3 ), reftype(&tv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test4 ), reftype(&tcv))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test5 ), reftype(t ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test6 ), reftype(tc ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test7 ), reftype(tv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test8 ), reftype(tcv ))) != sizeof(unsigned char)) << std::endl;
#if __cplusplus >= 201103L || defined(_MSC_VER) && _MSC_VER >= 1700
std::cerr << (sizeof(returns_reference(reftype(test9 ), reftype(t ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test10), reftype(tc ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test11), reftype(tv ))) != sizeof(unsigned char)) << std::endl;
std::cerr << (sizeof(returns_reference(reftype(test12), reftype(tcv ))) != sizeof(unsigned char)) << std::endl;
#endif
return 0;
}