Do not understand why declval<T> just adds an rvalue reference to T...

Hi,

I am reading the following which is not clear to me:

"For referenceable types, the return type is always an rvalue reference to the type, which allows declval to work with types that could not be returned from a function, such as abstract class types or array types." (C++ Templates, 2nd edition)

Will truly appreciate some light on this.

Thanks as always for your valuable help.

Juan Dent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <utility>

using array_type = char[30] ;
using function_type = int() ;
struct class_type { virtual int mem_fun() = 0 ; } ;

/*
// these declarations are errors: a function can't return a value of a non-copyable type

array_type foo_a() ; // *** error: function can't return an array (by value)
function_type foo_f() ; // *** error: function can't return a function (by value)
class_type foo_c() ; // *** error: function can't return an abstract type (by value)
*/

// these declarations are fine: a function can return a reference to any referenceable type

array_type&& bar_a() ; // fine
function_type&& bar_f() ; // fine
class_type&& bar_c() ; // fine

int main()
{
    std::cout << sizeof( bar_a() ) << ' ' << sizeof( bar_a()[5] ) << '\n' // 30 1
              << sizeof( std::declval<array_type>() ) << ' ' << sizeof( std::declval<array_type>()[5] ) << '\n' ; // 30 1
    
    /*
    // these are errors: strawman functions can only be used in unevaluated contexts

    auto&& bad = bar_a() ; // *** error: bar_a is not defined
    auto&& also_bad = std::declval<array_type>() ; // *** error: std::declval<T> is not defined
    */
}

http://coliru.stacked-crooked.com/a/a685ce87a1dea1c5
Ok, thank you JLBorges.
But just one question: why rvalue and not lvalue? why doesn't declval<T> return T&?
I understand that it cannot return by value, but not sure why it has to return by rvalue reference.

Thanks,
Juan Dent
> why rvalue and not lvalue? why doesn't declval<T> return T&?

The type of the result of std::declval<T>() may be T; T may be an lvalue reference type.

The result type of declval<T>() is typename std::add_rvalue_reference<T>::type

std::add_rvalue_reference<T>:
If T is an object type or a function type that has no cv- or ref- qualifier (since C++17), provides a member typedef type which is T&&, otherwise type is T. http://en.cppreference.com/w/cpp/types/add_reference


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <type_traits>
#include <utility>

template < typename T > using type_of_declval = decltype( std::declval<T>() ) ;

template < typename T > struct print_type ;
template <> struct print_type<int&> { print_type() { std::cout << "lvalue reference to int\n" ; } };
template <> struct print_type<const int&> { print_type() { std::cout << "lvalue reference to const int\n" ; } };
template <> struct print_type<int&&> { print_type() { std::cout << "rvalue reference to int\n" ; } };

int main()
{
    print_type< type_of_declval<int> > {} ; // rvalue reference to int ie. int&&

    print_type< type_of_declval<int&> > {} ; // lvalue reference to int ie. int&

    print_type< type_of_declval<const int&> > {} ; // lvalue reference to const int ie. const int&

    print_type< type_of_declval<int&&> > {} ; // rvalue reference to int ie. int&&

    // sizeof( &std::declval<int>() ) ; // *** error: can't take address of an rvalue
    // sizeof( &std::declval<int&&>() ) ; // *** error: can't take address of an rvalue

    // these are fine: take address of lvalue (result of std::declval in unevaluated contexts)
    std::cout << sizeof( &std::declval<int&>() ) << '\n' ; // sizeof( int* )
    std::cout << sizeof( &std::declval<const int&>() ) << '\n' ; // sizeof( const int* )
}

http://coliru.stacked-crooked.com/a/b124831ce762f7b0
http://rextester.com/BVFAT88601
>The type of the result of std::declval<T>() may be T;

I don't see this in your example: type_of_declval<int> is T&& not T. Neither of the other calls to type_of_decl<> results in T (always in lvalue or rvalue reference)...


Thanks
Juan
I don't see this in your example: type_of_declval<int> is T&& not T

when T is int&, type of declval is T
Hi Cubbi,

I can't prove this. I tried the following code:

 
using type = decltype(std::declval<int&>());               // type is int&, not int 


How can I find out what type is returned by declval<int&>?

Regards,
Juan
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <type_traits>
#include <utility>

int main()
{
    using T = int& ;
    
    if constexpr ( std::is_same_v< decltype( std::declval<T>() ), T > )
        std::cout << "type of std::declval<T>() is T\n" ;
}

http://coliru.stacked-crooked.com/a/67d8a2cfa477439d
Hi JLBorges,

As far as I can tell, your code only proves that the type of std::declval<T> is the same as T while T is int&. If you substitute T for int, the compile time if does not return true and the console does not receive any output. This is what I have been trying to say: the type of std::declval<int> is Not int, but int&.

Where is my mistake here? Is there an error in my statement?


Regards,
Juan Dent
Borges: The type of the result of std::declval<T>() may be T;
Dent: I don't see this in your example: Neither of the other calls to type_of_decl<> results in T
Cubbi: when T is int&, type of declval is T
Dent: I can't prove this.
Borges: some code which proves (for a second time) that when T is int&, type of std::declval<T>() is T
Dent: your code only proves that the type of std::declval<T> is the same as T while T is int&

Yes, that is all that it proves; that in some cases, the type of std::declval<T> is T
You are just going around in circles.


> This is what I have been trying to say: the type of std::declval<int> is Not int, but int&
> Where is my mistake here?

The type is wrong: the type of std::declval<int>() is int&& (xvalue of type int)
Last edited on
Sorry for the confusion JLBorges. I guess I did not explain myself correctly. declval allows one to simulate a default ctor even if none is available for a type. I get that. And I also get that for cases where objects cannot be copied (as in objects from abstract classes, arrays or function return types) so I understand declval must produce a reference. The thing I don't completely understand is why must the reference be && instead of just &...

Sorry again for my lack of clarity in my question.

Regards,
Juan Dent
> why must the reference be && instead of just &.

We should be able to use std::declval<T>() with any valid type T.
In particular, for some object type X, the type of std::declval<X&&>() must be an rvalue of type X

If std::declval<T>() returns an lvalue reference, ie. it was declared so:
template<class T>
typename std::add_lvalue_reference<T>::type declval() noexcept;
,
this wouldn't be the case because of the reference collapsing rules of C++ (T&& & yields T&).

With
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;
,
we get the right semantics:
T&& && yields T&& and T& && yields T&
ie. std::declval<X&&>() yields an rvalue of type X and std::declval<X&>() yields an lvalue of type X
Thank you JLBorges!!

Confusion ends here.

Best regards,
Juan Dent

BTW: which compiler do you use and which OS? Is it very fully compliant with C++17?

I use Visual Studio 2017 v15.5.6 and it is quite nice in this respect... getting better with every minor update...



> which compiler do you use and which OS?

These days, I use Windows for developing/testing portable standard C++ code.
I continue to use FreeBSD for playing with the snapshot builds of g++ and clang++

Windows:
MSYS2/mingw64 ports of g++ 7.3.0 and clang++ 5.0.1
Visual Studio (the Microsoft compiler and the clang++ front-end)

FreeBSD: the gcc-devel and clang-devel ports
(the current snapshots are: gcc-devel 7.3.1.s20180201 and clang-devel 7.0.d20180119)

I switched to Windows when FreeBSD clang-devel got stuck at 4.0.d20161207 for a long time.


> Is it very fully compliant with C++17?

g++ and clang++ are more or less fully C++17 compliant for the core language part; they are nowhere near fully C++17 compliant for the library part. Probably, with 8.x, g++ would be the first implementation come quite close to full conformance.

The microsoft compiler is not yet fully C++17 compliant; though they are somewhat ahead of the others in the library department.
Last edited on
Topic archived. No new replies allowed.