如何确定函数是否在C ++ 03中返回引用?

时间:2013-11-16 21:25:01

标签: c++ reference

在pre-C ++ 11中,当使用特定参数调用时,如何确定给定函数是否返回引用?

例如,如果代码如下所示:

template<class F>
bool returns_reference(F f) { return is_reference(f(5)); }

那我应该如何实施is_reference

请注意,f也可能是一个仿函数,其operator()可能有多个重载 - 我只关心实际通过我的参数调用的重载。

2 个答案:

答案 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。

它适用于大多数情况,但constvolatile的组合数量随参数数量呈指数增长。

每当它不起作用时 - 是否因为我们有多个参数或者重载决策是否相当模糊(例如,当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;
}