Is There a Reason Declval Returns Add_Rvalue_Reference Instead of Add_Lvalue_Reference

Is there a reason declval returns add_rvalue_reference instead of add_lvalue_reference

With add_rvalue_reference:

  • declval<Foo>() is of type Foo&&.
  • declval<Foo&>() is of type Foo& (reference collapsing: “Foo& &&” collapses to Foo&).
  • declval<Foo&&>() is of type Foo&& (reference collapsing: “Foo&& &&” collapses to Foo&&).

With add_lvalue_reference:

  • declval<Foo>() would be of type Foo&.
  • declval<Foo&>() would be of type Foo& (reference collapsing: “Foo& &” collapses to Foo&).
  • declval<Foo&&>() would be of type Foo& (!) (reference collapsing: “Foo&& &” collapses to Foo&).

that is, you would never get a Foo&&.

Also, the fact that declval<Foo>() is of type Foo&& is fine (you can write Foo&& rr = Foo(); but not Foo& lr = Foo();). And that declval<Foo&&>() would be of type Foo& just feels “wrong”!


Edit: Since you asked for an example:

#include <utility>
using namespace std;

struct A {};
struct B {};
struct C {};

class Foo {
public:
Foo(int) { } // (not default-constructible)

A onLvalue() & { return A{}; }
B onRvalue() && { return B{}; }
C onWhatever() { return C{}; }
};

decltype( declval<Foo& >().onLvalue() ) a;
decltype( declval<Foo&&>().onRvalue() ) b;
decltype( declval<Foo >().onWhatever() ) c;

If declval used add_lvalue_reference you couldn't use onRvalue() with it (second decltype).

How does std::declval<T>() work?

Basically, in a sizeof or decltype expression you can call functions that aren't implemented anywhere (they need to be declared, not implemented).

E.g.

class Silly { private: Silly( Silly const& ) = delete; };

auto foo() -> Silly&&;

auto main() -> int
{
sizeof( foo() );
}

The linker should not complain about that.

How does std::is_assignable work?

Here's the same code with some of the lexical obfuscation taken out:

 1043     template <typename Tp, typename Up>
1044 class is_assignable_helper
1046 {
1047 template <typename Tp1, typename Up1>
1048 static decltype(declval<Tp1>() = declval<Up1>(), one())
1049 test(int);
1050
1051 template<typename, typename>
1052 static two test(...);
1053
1054 public:
1055 static constexpr bool value = sizeof(test<Tp, Up>(0)) == 1;
1056 };

This class uses SFINAE to do the dirty work. That is, the value of variable value will depend on which test() function is chosen based on overload resolution. One overload takes an integer and the other takes a C variadic argument (specified by the ellipsis). If there is a substitution failure that occurs on the first overload, the second overload will be chosen.

If a substitution failure does occur, it will have come from the expression declval<Tp1>() = declval<Up1>(). declval<T>() is a function declaration that "returns" a value of the type std::add_rvalue_reference<T>::type. This function is mainly used in unevaluated contexts like decltype(), sizeof(), noexcept(), etc, in order to get an instance of a type without explicitly calling a constructor (because that type may not have an accessible constructor). If you'd like to know why add_rvalue_reference is the chosen return type, see this post.

Once you get an instance of the type, you can call member/non-member functions on those instances. The member function used is operator=(). If a class doesn't have an assignment operator (or has an inaccessible one) there will be a substitution failure. The fallback (variadic) version of test() will instead be chosen.

The reason for the difference in argument types (int vs ...) is because ... has the lowest conversion rank and it acts as a "last resort" for overload resolution. We can't just leave the parameters empty or else we'll get a redeclaration error.

As for the return type of test - if a substitution failure does not occur (a value of type Up can be assigned to a value of type Tp) then test() returns a type indicating success. If a substitution failure does happen, the the fallback version that returns a type indicating failure is chosen. These types are differentiated through a check for their sizes. We check for success by comparing with 1.

result_of doesn't work for me

result_of is result_of<Fn(ArgTypes...)>, not just result_of<Fn>;

Try

typedef typename result_of<decltype(&f)()>::type result_free;
typedef typename result_of<decltype(&asd::f)(asd)>::type result_mem;

(works with gcc 4.6.2)

How do I conditionally install a file with WiX based on the target machine (32bit or 64bit)?

You could probably just do two separate components, using the conditions

 VersionNT64

and

 Not VersionNT64

But the correct way is to compile separate installers for 32 and 64 bit OS's and 64-bit components should also be marked as such (with Win64="yes") - I don't know if it's even possible to mark a Windows Installer package as targeted towards both Intel and x64. Only time I've ever done a mixed architecture installation is with NSIS, all my WiX installers have separate installers for each architecture.



Related Topics



Leave a reply



Submit