gpt4 book ai didi

c++ - 在没有 decltype 的情况下声明时,函数定义中 decltype 中的依赖类型或参数无法编译

转载 作者:IT老高 更新时间:2023-10-28 12:49:03 28 4
gpt4 key购买 nike

我一直在使用解析为与声明相同类型的定义中的推导返回类型。这有效:

template <typename>
struct Cls {
static std::size_t f();
};

template <typename T>
decltype(sizeof(int)) Cls<T>::f() { return 0; }

但是,如果我通过替换 sizeof(int) 将定义更改为应该等效的内容与 sizeof(T) it fails
template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }

gcc 的错误(clang 几乎相同):
error: prototype for ‘decltype (sizeof (T)) Cls<T>::f()’ does not match any in class ‘Cls<T>’
decltype(sizeof(T)) Cls<T>::f() { return 0; }
^~~~~~
so.cpp:4:24: error: candidate is: static std::size_t Cls<T>::f()
static std::size_t f();
^

函数参数类型也会出现同样的问题:
template <typename>
struct Cls {
static void f(std::size_t);
};

template <typename T>
void Cls<T>::f(decltype(sizeof(T))) { } // sizeof(int) works instead

更奇怪的是,如果声明和定义匹配并且都使用 decltype(sizeof(T))它编译成功,我可以 static_assert返回类型是 size_t .以下编译成功:
#include <type_traits>

template <typename T>
struct Cls {
static decltype(sizeof(T)) f();
};

template <typename T>
decltype(sizeof(T)) Cls<T>::f() { return 0; }

static_assert(std::is_same<std::size_t, decltype(Cls<int>::f())>{}, "");

更新另一个例子。这不是依赖类型,但仍然失败。
template <int I>
struct Cls {
static int f();
};

template <int I>
decltype(I) Cls<I>::f() { return I; }

如果我使用 decltype(I)如果我使用 int 在定义和声明中都有效在定义和声明中它都有效,但两者不同则失败。

更新 2:
一个类似的例子。如 Cls改成不是类模板,编译成功。
template <typename>
struct Cls {
static int f();
using Integer = decltype(Cls::f());
};

template <typename T>
typename Cls<T>::Integer Cls<T>::f() { return I; }

更新 3:
另一个失败的例子来自 M.M.非模板化类的模板化成员函数。
struct S {
template <int N>
int f();
};

template <int N>
decltype(N) S::f() {}

为什么声明和定义仅与依赖类型不一致是非法的?为什么即使类型本身不像 template <int I> 那样依赖它也会受到影响以上?

最佳答案

因为当涉及到模板参数时,decltype根据标准返回一个独特的依赖类型,见下文。如果没有模板参数,那么它解析为一个明显的 size_t .因此,在这种情况下,您必须选择声明和定义都具有独立表达式(例如 size_t/decltype(sizeof(int)) )作为返回类型,或者两者都具有依赖表达式(例如 decltype(sizeof(T)) ),它们解析为唯一的依赖类型并考虑是等价的,如果它们的表达式是等价的(见下文)。

在这篇文章中,我使用了 C++ 标准草案 N3337 .

§ 7.1.6.2 [dcl.type.simpl]

¶ 4

The type denoted by decltype(e) is defined as follows: — if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded func- tions, the program is ill-formed;

— otherwise, if e is an xvalue, decltype(e) is T&&, where T is the type of e;

— otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;

— otherwise, decltype(e) is the type of e.



这解释了什么是 decltype(sizeof(int)) .但是对于 decltype(sizeof(T))还有另一部分解释它是什么。

§ 14.4 [temp.type]

¶ 2

If an expression e involves a template parameter, decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (14.5.6.1). [ Note: however, it may be aliased, e.g., by a typedef-name. — end note ]



在文件 lib/AST/Type.cpp 中的 Clang LLVM 源版本 3.9 中
DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
// C++11 [temp.type]p2: "If an expression e involves a template parameter,
// decltype(e) denotes a unique dependent type." Hence a decltype type is
// type-dependent even if its expression is only instantiation-dependent.
: Type(Decltype, can, E->isInstantiationDependent(),
E->isInstantiationDependent(),
E->getType()->isVariablyModifiedType(),
E->containsUnexpandedParameterPack()),

重要的短语以“Hence a decltype...”开头。它再次澄清了情况。

再次在文件 lib/AST/ASTContext.cpp 中的 Clang 源 3.9 版中
QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
DecltypeType *dt;

// C++11 [temp.type]p2:
// If an expression e involves a template parameter, decltype(e) denotes a
// unique dependent type. Two such decltype-specifiers refer to the same
// type only if their expressions are equivalent (14.5.6.1).
if (e->isInstantiationDependent()) {
llvm::FoldingSetNodeID ID;
DependentDecltypeType::Profile(ID, *this, e);

void *InsertPos = nullptr;
DependentDecltypeType *Canon
= DependentDecltypeTypes.FindNodeOrInsertPos(ID, InsertPos);
if (!Canon) {
// Build a new, canonical typeof(expr) type.
Canon = new (*this, TypeAlignment) DependentDecltypeType(*this, e);
DependentDecltypeTypes.InsertNode(Canon, InsertPos);
}
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, QualType((DecltypeType *)Canon, 0));
} else {
dt = new (*this, TypeAlignment)
DecltypeType(e, UnderlyingType, getCanonicalType(UnderlyingType));
}
Types.push_back(dt);
return QualType(dt, 0);
}

所以你看到 Clang 收集并挑选了 decltype 的那些独特的依赖类型。在/来自一个特殊的集合。

为什么编译器这么笨,竟然看不到 decltype的表达式是 sizeof(T)那总是 size_t ?是的,这对人类读者来说是显而易见的。但是当你设计和实现一个正式的语法和语义规则时,尤其是对于像 C++ 这样复杂的语言,你必须把问题分组并为它们定义规则,而不是仅仅为每个特定的问题想出一个规则,在后者这样你就无法随着你的语言/编译器设计而移动。这里同样没有公正的规则:if decltype有一个不需要任何模板参数解析的函数调用表达式 - 解析 decltype到函数的返回类型。不仅如此,您还需要涵盖很多情况,因此您提出了一个更通用的规则,就像上面引用的标准( 14.4[2] )一样。

此外,还有一个与 auto 类似的非明显案例。 , decltype(auto)由 AndyG 在 C++-14 中发现(N4296,§ 7.1.6.4 [dcl.spec.auto] 12/13):

§ 7.1.6.4 [dcl.spec.auto]

¶ 13

Redeclarations or specializations of a function or function template with a declared return type that uses a placeholder type shall also use that placeholder, not a deduced type. [ Example:

auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()
decltype(auto) f(); // error, auto and decltype(auto) don’t match


C++17 中的更改,文档编号 >= N4582

2016 年 3 月标准草案 N4582 的变化(感谢 bogdan)概括了以下声明:

§ 17.4 (old § 14.4) [temp.type]

¶ 2

If an expression e is type-dependent (17.6.2.2), decltype(e) denotes a unique dependent type. Two such decltype-specifiers refer to the same type only if their expressions are equivalent (17.5.6.1). [ Note: however, such a type may be aliased, e.g., by a typedef-name. — end note ]



这种变化导致了描述依赖于类型的表达式的另一部分,这对于我们的特殊情况看起来很奇怪。

§ 17.6.2.2 [temp.dep.expr] (old § 14.6.2.2)

¶ 4

Expressions of the following forms are never type-dependent (because the type of the expression cannot be dependent):

...
sizeof ( type-id )
...


还有更多关于值依赖表达式的部分,其中 sizeof如果 type-id 可以依赖于值依赖。值依赖表达式与 decltype 之间没有关系.想了想,没找到原因 decltype(sizeof(T))不得或不能解析为 size_t .而且我认为这是编译器开发人员不太注意的标准中非常偷偷摸摸的更改(“涉及模板参数”到“类型相关”)(可能被许多其他更改所淹没,也许不认为它可能实际上改变一些东西,只是一个简单的配方改进)。更改确实有意义,因为 sizeof(T)不依赖于类型,而是依赖于值。 decltype(e)的操作数是一个未计算的操作数,即不关心值,只关心类型。这就是为什么 decltype仅当 e 时才返回唯一类型是类型相关的。 sizeof(e)可能只是值(value)依赖。

我用 clang 5, gcc 8 -std=c++1z 尝试了代码- 相同的结果:错误。我更进一步并尝试了以下代码:
template <typename>
struct Cls {
static std::size_t f();
};

template <typename T>
decltype(sizeof(sizeof(T))) Cls<T>::f() {
return 0;
}

给出了同样的错误,即使是 sizeof(sizeof(T))既不依赖于类型,也不依赖于值(见 this post)。这让我有理由假设编译器正在以 C++-11/14 标准的旧方式工作(即“涉及模板参数”),就像上面来自 clang 3.9 源代码的源代码片段(我可以验证最新开发的clang 5.0 具有相同的行,在标准中没有发现与此新更改相关的任何内容),但不依赖于类型。

关于c++ - 在没有 decltype 的情况下声明时,函数定义中 decltype 中的依赖类型或参数无法编译,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44294743/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com