C++03. Test for rvalue-vs-lvalue at compile-time, not just at runtime

前端 未结 2 2001
耶瑟儿~
耶瑟儿~ 2020-12-08 20:46

In C++03, Boost\'s Foreach, using this interesting technique, can detect at run-time whether an expression is an lvalue or an rvalue. (I found that via this StackOv

相关标签:
2条回答
  • 2020-12-08 21:05

    It took some effort, but here's a tested and working is_lvalue macro that correctly handles const struct S function return types. It relies on const struct S rvalues not binding to const volatile struct S&, while const struct S lvalues do.

    #include <cassert>
    
    template <typename T>
    struct nondeducible
    {
      typedef T type;
    };
    
    char (& is_lvalue_helper(...))[1];
    
    template <typename T>
    char (& is_lvalue_helper(T&, typename nondeducible<const volatile T&>::type))[2];
    
    #define is_lvalue(x) (sizeof(is_lvalue_helper((x),(x))) == 2)
    
    struct S
    {
      int i;
    };
    
    template <typename T>
    void test_()
    {
      T a = {0};
      T& b = a;
      T (* c)() = 0;
      T& (* d)() = 0;
      assert (is_lvalue(a));
      assert (is_lvalue(b));
      assert (!is_lvalue(c()));
      assert (is_lvalue(d()));
    }
    
    template <typename T>
    void test()
    {
      test_<T>();
      test_<const T>();
      test_<volatile T>();
      test_<const volatile T>();
    }
    
    int main()
    {
      test<int>();
      test<S>();
    }
    

    Edit: unnecessary extra parameter removed, thanks Xeo.

    Edit again: As per the comments, this works with GCC but relies on unspecified behaviour in C++03 (it's valid C++11) and fails some other compilers. Extra parameter restored, which makes it work in more cases. const class rvalues give a hard error on some compilers, and give the correct result (false) on others.

    0 讨论(0)
  • 2020-12-08 21:05

    The address-of operator (&) can only be used with an lvalue. So if you used it in an SFINAE test, you could distinguish at compile-time.

    A static assertion could look like:

    #define STATIC_ASSERT_IS_LVALUE(x) ( (sizeof &(x)), (x) )
    

    A trait version might be:

    template<typename T>
    struct has_lvalue_subscript
    {
        typedef char yes[1];
        typedef char no[2];
    
        yes fn( char (*)[sizeof (&(((T*)0)->operator[](0))] );
        no fn(...);
        enum { value = sizeof(fn(0)) == 1 };
    };
    

    and could be used like

    has_lvalue_subscript< std::vector<int> >::value
    

    (Warning: not tested)

    I can't think of any way to test an arbitrary expression valid in the caller's context, without breaking compilation on failure.

    0 讨论(0)
提交回复
热议问题