Sitemap
W

W

I'm a software engineer with a passion for technology, science, and lifelong learning.

C++ 9

2 min readOct 24, 2026

--

For a local variable T x;, what does decltype((x)) y = x; declare, and why is it a classic interview gotcha?

Answer: It declares an lvalue reference to T, binding directly to x with no copy

This question tests a special rule of decltype: adding parentheses can change the resulting type.

Consider:

T x;
decltype((x)) y = x;

The result is:

T& y = x;

So y is an lvalue reference bound directly to x. No new T object is copied.

The gotcha is that these two expressions produce different types:

decltype(x)     // T
decltype((x)) // T&

Why?

decltype has two sets of rules.

When its argument is an unparenthesized variable name, such as:

decltype(x)

C++ uses a special rule: the result is the declared type of x.

If:

T x;

then:

decltype(x)   // T

Therefore:

decltype(x) y = x;

is equivalent to:

T y = x;      // copy

y is a separate object.

But once you write:

decltype((x))

the extra parentheses mean the special “variable name” rule no longer applies. Now decltype examines the value category of the expression (x).

Get W’s stories in your inbox

Join Medium for free to get updates from this writer.

A named variable is an lvalue expression, and the general decltype rule is:

expression is lvalue  → T&
expression is xvalue → T&&
expression is prvalue → T

Since:

(x)

is an lvalue expression of type T:

decltype((x))   // T&

Therefore:

decltype((x)) y = x;

means:

T& y = x;

You can see the difference by modifying y

For example:

int x = 10;
decltype((x)) y = x; // int& y = x
y = 99;
std::cout << x; // 99

The relationship is:

x
┌──────────┐
│ 10 │
└──────────┘
▲
│
y

y is simply another name for x.

Compare that with:

int x = 10;
decltype(x) y = x; // int y = x
y = 99;
std::cout << x; // 10

Now there are two independent objects:

x                 y
┌──────┐ ┌──────┐
│ 10 │ │ 99 │
└──────┘ └──────┘

Why is this an interview gotcha?

Because intuitively:

x

and:

(x)

appear to be exactly the same expression. In almost every ordinary context, adding those parentheses changes nothing.

But decltype deliberately distinguishes them:

T x;
decltype(x) // T
decltype((x)) // T&

This becomes especially important in generic code. For example:

auto f() -> decltype(auto) {
int x = 42;
return (x); // dangerous!
}

Because the returned expression is (x):

decltype((x))   // int&

so decltype(auto) deduces the return type as:

int&

But x is local and is destroyed when f() returns, so the function returns a dangling reference.

The key rule worth memorizing is:

decltype(name)
→ declared type of name

decltype((expression))
→ inspect value category:

lvalue → T&
xvalue → T&&
prvalue → T

So for this question:

T x;
decltype((x)) y = x;

(x) is an lvalue, therefore decltype((x)) is T&, and y binds directly to x without making a copy.

--

--

W
W

Written by W

I'm a software engineer with a passion for technology, science, and lifelong learning.