GCC 对 std::invocable 的实施是否不正确或仍然不完整?
Is GCC's implementation of std::invocable incorrect or still incomplete?
我尝试在 godbolt 上可用的以下代码片段中使用 std::is_invocable。
#include <type_traits>
struct A {
void operator()(int);
};
struct B {
void operator()(int, int);
};
struct C : A, B {};
int main()
{
static_assert(std::is_invocable<A, int>() == true);
static_assert(std::is_invocable<B, int>() == false);
static_assert(std::is_invocable<C, int>() == true);
}
Clang 可以解决所有问题,但 GCC 似乎对这一行有问题:
static_assert(std::is_invocable<C, int>() == true);
C 继承了函数调用运算符。
那么 GCC 的实现不正确或仍在进行中?
GCC 在这里实际上是正确的。如果我们遵循 [class.member.lookup] 中的规则,我们首先有
The lookup set for N in C, called S(N,C), consists of two component sets: the declaration set, a set of members named N; and the subobject set, a set of subobjects where declarations of these members were found (possibly via using-declarations). In the declaration set, type declarations (including injected-class-names) are replaced by the types they designate.
S(N,C) is calculated as follows:
因此我们将为 class(在本例中也命名为 C)
构建一组名称 S(N, C)
我们进入下一段
The declaration set is the result of a single search in the scope of C for N from immediately after the class-specifier of C if P is in a complete-class context of C or from P otherwise. If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.
并且当我们在 C
中查找 operator()
时,我们没有找到任何内容,因此 S(N, C)
是空的。然后我们继续下一段
Otherwise (i.e., C does not contain a declaration of N or the resulting declaration set is empty), S(N,C) is initially empty. Calculate the lookup set for N in each direct non-dependent ([temp.dep.type]) base class subobject Bi, and merge each such lookup set S(N,Bi) in turn into S(N,C).
因此,我们将遍历每个碱基并将其查找集添加到 S(N, C)
中,这将我们带到
的下一段
Otherwise, if the declaration sets of S(N,Bi) and S(N,C) differ, the merge is ambiguous: the new S(N,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent merges, an invalid declaration set is considered different from any other.
因此,由于 C
中没有 operator()
,但它在基数中,因此我们现在有包含基数 class [=11= 的无效声明集]的。那么我们有
he result of the search is the declaration set of S(N,T). If it is an invalid set, the program is ill-formed. If it differs from the result of a search in T for N from immediately after the class-specifier of T, the program is ill-formed, no diagnostic required.
这告诉我们无效的声明集格式错误,因此它是一个错误,应该无法编译。
解决方法是使用像
这样的 using 语句将基础 class 函数带入派生的 class 作用域
struct C : A, B {
using A::operator();
using B::operator();
};
现在将名称带入 C
,这将为您提供一个有效的名称集,然后可以将其传递给重载解析,以便它选择正确的重载。
我尝试在 godbolt 上可用的以下代码片段中使用 std::is_invocable。
#include <type_traits>
struct A {
void operator()(int);
};
struct B {
void operator()(int, int);
};
struct C : A, B {};
int main()
{
static_assert(std::is_invocable<A, int>() == true);
static_assert(std::is_invocable<B, int>() == false);
static_assert(std::is_invocable<C, int>() == true);
}
Clang 可以解决所有问题,但 GCC 似乎对这一行有问题:
static_assert(std::is_invocable<C, int>() == true);
C 继承了函数调用运算符。
那么 GCC 的实现不正确或仍在进行中?
GCC 在这里实际上是正确的。如果我们遵循 [class.member.lookup] 中的规则,我们首先有
The lookup set for N in C, called S(N,C), consists of two component sets: the declaration set, a set of members named N; and the subobject set, a set of subobjects where declarations of these members were found (possibly via using-declarations). In the declaration set, type declarations (including injected-class-names) are replaced by the types they designate. S(N,C) is calculated as follows:
因此我们将为 class(在本例中也命名为 C)
构建一组名称 S(N, C)我们进入下一段
The declaration set is the result of a single search in the scope of C for N from immediately after the class-specifier of C if P is in a complete-class context of C or from P otherwise. If the resulting declaration set is not empty, the subobject set contains C itself, and calculation is complete.
并且当我们在 C
中查找 operator()
时,我们没有找到任何内容,因此 S(N, C)
是空的。然后我们继续下一段
Otherwise (i.e., C does not contain a declaration of N or the resulting declaration set is empty), S(N,C) is initially empty. Calculate the lookup set for N in each direct non-dependent ([temp.dep.type]) base class subobject Bi, and merge each such lookup set S(N,Bi) in turn into S(N,C).
因此,我们将遍历每个碱基并将其查找集添加到 S(N, C)
中,这将我们带到
Otherwise, if the declaration sets of S(N,Bi) and S(N,C) differ, the merge is ambiguous: the new S(N,C) is a lookup set with an invalid declaration set and the union of the subobject sets. In subsequent merges, an invalid declaration set is considered different from any other.
因此,由于 C
中没有 operator()
,但它在基数中,因此我们现在有包含基数 class [=11= 的无效声明集]的。那么我们有
he result of the search is the declaration set of S(N,T). If it is an invalid set, the program is ill-formed. If it differs from the result of a search in T for N from immediately after the class-specifier of T, the program is ill-formed, no diagnostic required.
这告诉我们无效的声明集格式错误,因此它是一个错误,应该无法编译。
解决方法是使用像
这样的 using 语句将基础 class 函数带入派生的 class 作用域struct C : A, B {
using A::operator();
using B::operator();
};
现在将名称带入 C
,这将为您提供一个有效的名称集,然后可以将其传递给重载解析,以便它选择正确的重载。