关于cppreference.com对decltype解释的2个问题
2 questions about cppreference.com's explanation of decltype
我读书的时候this online c++ reference page about decltype
我想知道这一段:
If expression is a function call which returns a prvalue of class
type or is a comma expression whose right operand is such a function call, a temporary object is not introduced for that prvalue.
(until C++17)
我的问题是:引入或不引入临时有关系吗?
这一段:
Note that if the name of an object is parenthesized, it is treated
as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types.
我的问题又来了:区别对待他们的理由是什么?
有没有人能帮我一把,让我在黑暗的角落里看到一些光。谢谢。
Introducing or not introducing a temporary, does that matter?
如果您查看使用函数结果的表达式,这会更有意义:
// given
template<typename T> struct Foo {};
template<typename T> Foo<T> foo();
template<typename T> void bar(const Foo<T>&);
// This:
bar(foo<int>());
// Is equivalent to:
{
Foo<int>&& tmp = foo<int>();
bar(tmp);
}
// So should tmp "exist" here?
using T = decltype(foo<int>());
您可以很容易地争辩说 foo<int>()
表达式实际上 等价于 到 Foo<int>&& tmp = foo<int>()
。如果 tmp
的存在在 decltype()
上下文中是不受欢迎的,那么它需要被弄清楚。
而tmp
在这里绝对是不可取的。它会导致创建 Foo<int>
专业化。但是仅仅因为您识别了一种类型并不意味着您实际上正在使用它。类型别名也是如此。
演示:(参见 godbolt)
#include <type_traits>
template<typename T>
struct Foo {
// Will cause a compile error if it's ever instantiated with int
static_assert(!std::is_same_v<T, int>);
};
using FooInt = Foo<int>;
template<typename T>
Foo<T> foo() {
return {};
}
void bar() {
// Does not cause Foo<int> to exist just yet
using T = decltype(foo<int>());
// This instantiates Foo<int> and causes the compile error
// T x;
}
What's the rationale behind treating them differently?
N.B. 以下更多是关于如何对此进行推理的指南。实际的技术细节是不同的,但它可能会变得相当复杂。
不要认为括号有什么特别之处。相反,将其视为 decltype(id-expression)
是特例。
假设我们有以下声明:int x;
x
表达式的行为不像 int
,而是 int&
。否则 x = 3;
没有意义。
尽管如此,decltype(x)
仍然是 int
。这是特殊情况:decltype(id-expression)
returns 标识符本身的类型而不是表达式的类型。
另一方面,(x)
的行为也类似于 int&
,但由于它不是 id-expression
,它被解释为任何正则表达式。所以 decltype((x))
是 int&
.
#include <type_traits>
int x = 0;
using T = decltype(x);
using U = decltype((x));
static_assert(std::is_same_v<T, int>);
static_assert(std::is_same_v<U, int&>);
我读书的时候this online c++ reference page about decltype
我想知道这一段:
If expression is a function call which returns a prvalue of class type or is a comma expression whose right operand is such a function call, a temporary object is not introduced for that prvalue. (until C++17)
我的问题是:引入或不引入临时有关系吗?
这一段:
Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types.
我的问题又来了:区别对待他们的理由是什么?
有没有人能帮我一把,让我在黑暗的角落里看到一些光。谢谢。
Introducing or not introducing a temporary, does that matter?
如果您查看使用函数结果的表达式,这会更有意义:
// given
template<typename T> struct Foo {};
template<typename T> Foo<T> foo();
template<typename T> void bar(const Foo<T>&);
// This:
bar(foo<int>());
// Is equivalent to:
{
Foo<int>&& tmp = foo<int>();
bar(tmp);
}
// So should tmp "exist" here?
using T = decltype(foo<int>());
您可以很容易地争辩说 foo<int>()
表达式实际上 等价于 到 Foo<int>&& tmp = foo<int>()
。如果 tmp
的存在在 decltype()
上下文中是不受欢迎的,那么它需要被弄清楚。
而tmp
在这里绝对是不可取的。它会导致创建 Foo<int>
专业化。但是仅仅因为您识别了一种类型并不意味着您实际上正在使用它。类型别名也是如此。
演示:(参见 godbolt)
#include <type_traits>
template<typename T>
struct Foo {
// Will cause a compile error if it's ever instantiated with int
static_assert(!std::is_same_v<T, int>);
};
using FooInt = Foo<int>;
template<typename T>
Foo<T> foo() {
return {};
}
void bar() {
// Does not cause Foo<int> to exist just yet
using T = decltype(foo<int>());
// This instantiates Foo<int> and causes the compile error
// T x;
}
What's the rationale behind treating them differently?
N.B. 以下更多是关于如何对此进行推理的指南。实际的技术细节是不同的,但它可能会变得相当复杂。
不要认为括号有什么特别之处。相反,将其视为 decltype(id-expression)
是特例。
假设我们有以下声明:int x;
x
表达式的行为不像 int
,而是 int&
。否则 x = 3;
没有意义。
尽管如此,decltype(x)
仍然是 int
。这是特殊情况:decltype(id-expression)
returns 标识符本身的类型而不是表达式的类型。
另一方面,(x)
的行为也类似于 int&
,但由于它不是 id-expression
,它被解释为任何正则表达式。所以 decltype((x))
是 int&
.
#include <type_traits>
int x = 0;
using T = decltype(x);
using U = decltype((x));
static_assert(std::is_same_v<T, int>);
static_assert(std::is_same_v<U, int&>);