C++ 中的类可以定义一个或多个转换运算符。其中一些可以自动推导结果类型:operator auto。所有编译器都允许程序员将任何运算符标记为已删除operator auto。对于具体类型,删除意味着尝试调用此类转换将导致编译错误。但这样做的目的可能是什么operator auto() = delete?
考虑一个例子:
struct A {
operator auto() = delete;
};
struct B : A {
operator auto() { return 1; }
};
int main() {
B b;
A a = b; // error in Clang
int i = b; // error in Clang and GCC
int j = a; // error in Clang and GCC and MSVC
}
Run Code Online (Sandbox Code Playgroud)
由于编译器无法推断出结果类型,因此它实际上禁止从此类或派生类进行任何转换,并出现错误:
function 'operator auto' with deduced return type cannot be …Run Code Online (Sandbox Code Playgroud) 众所周知,throw可以作为C++三元运算符的第二个或第三个操作数放置?:。但是它可以在操作数的逗号子表达式中吗?看起来编译器在这方面存在分歧。请考虑一个例子:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b )
(std::cout << "smth\n", throw 0); //ok everywhere
i = b ? 2 : (std::cout << "smth\n", throw 0); //ok in MSVC only
};
Run Code Online (Sandbox Code Playgroud)
这个例子被 MSVC 接受,但被 GCC 和 Clang 拒绝,演示:https : //gcc.godbolt.org/z/6q46j5exP
虽然错误信息:
#include <iostream>
void foo(bool b) {
int i = b ? 1 : (throw 0); //ok everywhere
if ( !b …Run Code Online (Sandbox Code Playgroud) 考虑一个 C++20 程序,其中函数中foo有一个结构化绑定auto [y]。函数返回y,它被转换为对象类型A。A可以从右值引用的常量引用构造。
#include <tuple>
#include <iostream>
struct A {
A(const int &) { std::cout << "A(const int &) "; }
A(int &&) { std::cout << "A(int &&) "; }
};
A foo() {
auto [y] = std::make_tuple(1);
return y;
}
int main() { foo(); }
Run Code Online (Sandbox Code Playgroud)
根据C++20语言标准应该选择哪一个构造函数?
Clang 选择A(const int &)和 GCC 选择A(int &&),演示:https : //gcc.godbolt.org/z/5q779vE6T
是否有一个编译器不支持这方面的标准?
大家都知道thisC++中的对象指针在其方法中是不能改变的。但是对于可变的 lambda,在何处this捕获,一些当前的编译器提供了这种可能性。考虑这个代码:
struct A {
void foo() {
//this = nullptr; //error everywhere
(void) [p = this]() mutable {
p = nullptr; //#1: ok everywhere
(void)p;
};
(void) [this]() mutable {
this = nullptr; //#2: ok in MSVC only
};
}
};
Run Code Online (Sandbox Code Playgroud)
在第一个 lambda 中this被捕获并赋予一个新名称p。这里所有的编译器都允许更改p. 在第二个 lambda 中this被自己的名字捕获,只有 MSVC 允许程序员改变它的值。演示:https : //gcc.godbolt.org/z/x5P81TT4r
我相信 MSVC 在第二种情况下行为不正确(尽管它看起来像一个不错的语言扩展)。谁能从标准中找到正确的措辞(搜索并不容易,因为该词this在那里被提及了 2800 多次)?
C++20 中的约束在通过将它们划分为原子约束来检查是否满足之前进行标准化。例如,约束E = E1 || E2有两个原子约束E1和E2
原子约束中的替换失败应被视为原子约束的假值。
如果我们考虑一个示例程序,则会concept Complete = sizeof(T)>0检查正在定义的类T:
template<class T>
concept Complete = sizeof(T)>0;
template<class T, class U>
void f() requires(Complete<T> || Complete<U>) {}
template<class T, class U>
void g() requires(sizeof(T)>0 || sizeof(U)>0) {}
int main() {
f<void,int>(); //ok everywhere
g<void,int>(); //error in Clang
}
Run Code Online (Sandbox Code Playgroud)
那么该函数f<void,int>()满足要求,因为由于替换失败而Complete<void>计算为,并且计算为。falseComplete<int>true
但相似的函数g<void,int>()会使编译器产生分歧。GCC 接受它,但 Clang 不接受:
error: no matching function for call to 'g'
note: …Run Code Online (Sandbox Code Playgroud) 参数类型相同的静态和非静态成员函数不能重载。但是,如果成员函数是模板并且其中之一有requires子句,那么所有编译器都允许它。但是当调用这两个成员函数时就会出现问题:
struct A {
static int f(auto) { return 1; }
int f(auto) requires true { return 2; }
};
int main() {
[[maybe_unused]] int (A::*y)(int) = &A::f; // ok everywhere (if no below line)
[[maybe_unused]] int (*x)(int) = &A::f; //ok in GCC and Clang (if no above line)
}
Run Code Online (Sandbox Code Playgroud)
如果只剩下一行(任意),main()则 GCC 和 Clang 接受该程序。但是当两行都main()存在时,Clang 会打印
error: definition with same mangled name '_ZN1A1fIiEEiT_' as another definition
Run Code Online (Sandbox Code Playgroud)
GCC 报告内部编译器错误。演示: https: //gcc.godbolt.org/z/4c1z7fWvx
所有编译器在接受struct …
在下一个例子中,U带有私有析构函数的类有一个友元函数foo。这个友元函数的参数类型U为默认值U{}:
class U{ ~U(); friend void foo(U); };
void foo(U = {});
Run Code Online (Sandbox Code Playgroud)
Clang 和 MSVC 接受此代码,但 GCC 拒绝它并显示错误
error: 'U::~U()' is private within this context
2 | void foo(U = {});
| ^
Run Code Online (Sandbox Code Playgroud)
演示:https : //gcc.godbolt.org/z/eGxYGdzj3
哪个编译器就在这里,友谊是否扩展到 C++ 中的默认参数?
众所周知,仅返回类型不同的普通函数不能在 C++ 中重载。
但这个限制不适用于重载的函数模板,例如:
int f(auto) { return 1; }
auto f(auto) { return 2; }
Run Code Online (Sandbox Code Playgroud)
所有编译器都接受它,演示: https: //gcc.godbolt.org/z/qj73Mzehd
为什么该语言对模板做出这样的例外?
如果重载函数的返回类型不同,则可以使用强制转换为预期函数类型来选择其中一个函数。令人惊讶的是,即使返回类型实际上相同,Clang 也允许解决歧义,例如:
((int(*)(int))f)(3);
Run Code Online (Sandbox Code Playgroud)
选择
int f(auto) { return 1; }
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/snfvbq1ME
Clang这里错了吗?
从C++20开始,编译器可以通过operator ==() = default语法的方式自动生成用户类的比较运算符。但是这个运算符必须只能在类定义内部默认吗?还是也可以在类定义之后?
考虑该程序:
struct A { friend bool operator==(A,A); };
bool operator==(A,A) = default;
Run Code Online (Sandbox Code Playgroud)
它被 GCC 接受,但被 Clang 拒绝,并出现错误:
error: equality comparison operator can only be defaulted in a class definition
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/KboK7frhb
这里是哪个编译器?
例如,将运算符定义放在类定义之外对于仅将运算符放在一个翻译单元中非常有用,从而缩短了大程序的编译时间。
一个 C++ 函数可以有多个参数包。虽然看起来不太实用,但了解它们的语言规则仍然很有趣。
例如,如果有两个重载:
constexpr int f(auto...) { return 1; }
constexpr int f(auto..., auto...) { return 2; }
Run Code Online (Sandbox Code Playgroud)
f不带参数的调用f()在 MSVC 中选择版本 1,在 Clang 中选择版本 2,ambiguous overloaded call在 GCC 中选择版本 2。
如果f使用参数调用f(1),则 MSVC 和 GCC 都选择版本 1,而 Clang 仍选择版本 2。
演示: https: //gcc.godbolt.org/z/PWr6h1dn1
这里是哪个编译器?
有一个类似的问题带有两个参数包的函数模板重载解析,但是
c++ language-lawyer overload-resolution c++20 parameter-pack
c++ ×10
language-lawyer ×10
c++20 ×6
c++-concepts ×1
friend ×1
lambda ×1
overloading ×1
static ×1
templates ×1
this ×1
throw ×1