decltype 不会推断 const 对象的 const 成员

decltype does not deduce const members for const objects

本文关键字:const 对象 成员 decltype      更新时间:2023-10-16
#include <type_traits>
#include <functional>
struct Chains
{};
struct Stages
{
Chains mutating_chains;
Chains sideffect_chains;
Chains write_chains;
void forall_chains(const std::function<void(Chains & chain)> & fun)
{
forall_chains(*this, fun);
}
void forall_chains(
const std::function<void(const Chains & chain)> & fun) const
{
forall_chains(*this, fun);
}
template <typename Self>
static void forall_chains(
Self & self,
const std::function<void(decltype(self.mutating_chains) & chain)> & fun)
{
fun(self.mutating_chains);
fun(self.sideffect_chains);
fun(self.write_chains);
}
};

显然有些事情我无法理解decltype.因为根据编译器抛出的错误消息,Self 被推导为 const Stages,那么为什么 self.member 不被推导为 const 成员呢?还有如何使其正常工作,推断常量对象的常量成员?我在表达式decltype((self.mutating_chains))中添加了括号,并通过了编译,但我不确定这是否是正确的做法。

f.cpp: In instantiation of ‘static void Stages::forall_chains(Self&, const std::function<void(decltype (self.mutating_chains)&)>&) [with Self = const Stages; decltype (self.mutating_chains) = Chains]’:
f.cpp:150:33:   required from here
f.cpp:158:33: error: no match for call to ‘(const std::function<void(Chains&)>) (const Chains&)’
fun(self.mutating_chains);

我在表达式decltype((self.mutating_chains))中添加了括号并通过了编译,但我不确定这是否是正确的做法。

是的,在这种情况下,这是正确的做法。简而言之,decltype(x)为您提供了声明的x类型,它不依赖于表达式的值类别。

对于类型为T左值表达式xdecltype((x))反而产生T&,这在您的情况下会正确应用const限定符。

您可以在 cpp首选项页面上找到更正式(和准确)的解释decltype(...).


顺便说一下,请考虑通过模板参数而不是std::function传递回调。后者不是零成本抽象 - 它是一个使用类型擦除的重量级包装器,应将其使用量降至最低。

template <typename Self, typename F>
static void forall_chains(Self& self,  F&& fun){ /* ... */ }

我写了一篇关于这个主题的文章:将函数传递给函数

是的,确实如此。decltype有一个decltype(self.mutating_chains)的特殊情况(强调我的):

如果参数是无括号的 id 表达式或无括号的类成员访问表达式,则 decltype 将生成此表达式命名的实体的类型。

因此,您将获得声明self.mutating_chains的实际类型,即Chains

添加括号时,您将回退到一般情况,该情况实际上计算表达式的类型,在const Self的情况下,表达式按预期const Chains &

如果参数是T和 a) 的任何其他表达式 [...]

b) 如果表达式的值类别是左值,则 decltype 产生T&;
c) [...]