declval()() – what does this mean in the below context?

declval()() – what does this mean in the below context?

18

This is from: https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/include/std/type_traits

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
...
  template<typename _Tp1, typename _Tp2>
    struct __common_reference_impl<_Tp1, _Tp2, 3,
                   void_t<__cond_res<_Tp1, _Tp2>>>
    { using type = __cond_res<_Tp1, _Tp2>; };

I’m trying to figure out what _Xp(&)() is – is it a function call signature? i.e. a constructor? Doesn’t really make sense. It seems there is a anonymous variable name there, i.e.:

_Xp(&anon)()

I still can’t wrap my head around it somehow and I’ve been coding C++ for the last 34 years.

Any explanation is appreciated. Thanks.

4

  • I would interpret it as function type declarations, where _Xp and _Yp are template type parameters, the ampersands indicate that they are references to functions of the type _Xp or _Yp, and () means that they have no input arguments.

    – Tomasz Kasperczyk

    yesterday

  • 1

    It's a type: a reference to a function that takes no parameters and returns an _Xp.

    – Sam Varshavchik

    yesterday

  • 1

    Pretty similar to the type of a function pointer _Xp(*)(); just using a reference, not a pointer…

    – fabian

    yesterday

  • 1

    @SamVarshavchik (and all): My thought is that this is syntactic sugar to generate whatever C++ value category is represented by a method returning by value an _Xp. As a side question: What value category is a C++ return-by-value? It's not an rvalue-reference and I looked at en.cppreference.com/w/cpp/language/value_category and I couldn't easily figure it out.

    – David Bien

    yesterday

4 Answers
4

Highest score (default)

Trending (recent votes count more)

Date modified (newest first)

Date created (oldest first)

24

tl;dr; We need a way to produce an expression with type and value category T, not type and value category T&&, so we can’t just use std::declval<T>() and instead need to do something else.


The point of this:

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());

is to give you the type of false ? x : y where x is an expression of type and value category _Xp and y is an expression of type and value category _Yp.

The conditional operator (usually called the ternary operator), ?:, is an extremely complicated language feature. It’s one of the places in the language where there is actually a differentiation between prvalues and xvalues.

The naive way to implement this would be:

  template<typename _Xp, typename _Yp>
    using __cond_res
      = decltype(false ? declval<_Xp>() : declval<_Yp>());

Because, well, isn’t that what declval<T>() is for, to give you a T? But actually, there’s a flaw here, because declval isn’t specified as:

template <typename T>
auto declval() -> T;

It’s specified as (add_rvalue_reference_t<T> rather than T&& to correctly handle void):

template <typename T>
auto declval() -> std::add_rvalue_reference_t<T>;

As a result, __cond_res<int, int> and __cond_res<int&&, int&&> would be indistinguishable, even though the first needs to be int while the latter needs to be int&&.


So, we need a way to actually produce an arbitrary expression of type T. One way would to just actually:

template <typename T>
auto better_declval() -> T;

template<typename _Xp, typename _Yp>
  using __cond_res
    = decltype(false ? better_declval<_Xp>() : better_declval<_Yp>());

This works.

An alternative is to produce an instance of a function that gives you T and then invoke it. That’s what declval<_Xp(&)()>()() does – gives you a reference to a nullary function that returns a _Xp, and then invokes it, giving you an _Xp (of the correct value category).

In this case, this seems like unnecessary complexity compared to the better_declval approach, but it turns out that this pattern is useful in other contexts as well. Like concepts:

template <typename T>
concept something = requires (T(&obj)()){
    f(obj());
};

Here, I have a concept that checks to see if I can call f with an expression of type T, including differentiating between prvalues and xvalues correctly. The above is the most convenient way I know of to achieve that goal. Which is, admittedly, unfortunate.

You could also do:

template <typename T>
concept something = requires {
    f(better_declval<T>());
};

It just depends on your perspective I guess, and how many times you need to use obj.

Once you’ve seen this T(&)() pattern used in the concept context, it’s a familiar pattern, so it makes sense to just use it consistently.

2

  • Asking this question again: What value category is a C++ return-by-value? I couldn't easily figure it out by looking at: en.cppreference.com/w/cpp/language/value_category

    – David Bien

    yesterday

  • 3

    @DavidBien Like given int foo();, what is the value category of foo()? It's a prvalue.

    – Barry

    yesterday

10

declval<_Xp(&)()>()() – what does this mean

_Xp(&)() is a reference to a function that takes no parameters.

declval<_Xp(&)()>() (meaning std::decvlval) is a hypothetical instance of this function.

declval<_Xp(&)()>()() is calling that hypothetical instance, producing a return value.

Collectively, it means "The value that would be returned by calling a function of type _Xp."

1

  • 1

    "The value that would be returned by calling a function of type _Xp." I think you mean of type _Xp()?

    – ildjarn

    19 hours ago

7

  • _Xp(&)() is a reference to a function taking no parameters and returning _Xp.
  • declval<_Xp(&)()>()() returns such a function reference and invokes it, which results in _Xp.
decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()())

… is a common type of _Xp and _Yp, following the rules of the conditional operator.

The difference to just using declval<_Xp>() is that declval doesn’t return a value, it returns std::add_rvalue_reference_t<_Xp>.

You can see that this type alias is being used to determine the common reference between two types:

template<typename _Tp1, typename _Tp2>
struct __common_reference_impl<_Tp1, _Tp2, 3, void_t<__cond_res<_Tp1, _Tp2>>>
{ using type = __cond_res<_Tp1, _Tp2>; };

Note: you can use cdecl+ to better understand C and C++ type syntax.

4

  • declval<_Xp>() (if _Xp isn't void) will never be a prvalue, always an xvalue or lvalue, so your simplified version would add a lot of rvalue references when it should just be values. Also this checks for arrays/functions that can't be the return type of a function (e.g., common_reference<int[3], int[3]> is int*, not int(&)[3] or int(&&)[3], and common_reference<int(), int()> is int(*)(), not int(&)())

    – Artyer

    yesterday

  • @Artyer As I commented above: My thought is that this is syntactic sugar to generate whatever C++ value category is represented by a method returning by value an _Xp. As a side question: What value category is a C++ return-by-value? It's not an rvalue-reference and I looked at en.cppreference.com/w/cpp/language/value_category and I couldn't easily figure it out.

    – David Bien

    yesterday

  • @DavidBien when a function returns a value, calling it is a prvalue expression. Returning references produces lvalues or xvalues depending on the kind of reference.

    – Jan Schultke

    yesterday

  • 1

    @JanSchultke thanks – it's first explanation under prvalue in cppref – I read it and somehow it didn't grok but now it does.

    – David Bien

    yesterday

0

_Xp(&)() is a reference to a function that takes no arguments and has a return type of _Xp. Here’s how you can confirm that:

#include <type_traits>

using Xp = int;

Xp foo() { return 0; }

int main() {
    auto& refFoo = foo;     // Deduced type
    static_assert(std::is_same_v<decltype(refFoo), Xp (&)()>);

    Xp (&refFoo2)() = foo;  // Explicitly specified type
    static_assert(std::is_same_v<decltype(refFoo2), Xp (&)()>);
}

Your Answer

Draft saved
Draft discarded

Post as a guest

Required, but never shown


By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged

or ask your own question.

Leave a Reply

Your email address will not be published. Required fields are marked *