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)
#include <iostream>
#include <utility>
using array_type = char[30] ;
using function_type = int() ;
struct class_type { virtualint 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
*/
}
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.
> 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
>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)...
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?
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)
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.
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
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.