C++23 允许[[...]]lambda 表达式中的属性:
auto lambda = [] [[nodiscard]]
{
return 42;
};
Run Code Online (Sandbox Code Playgroud)
但是语法有两个属性列表,大致在参数列表之前和之后:
auto lambda = [] [[nodiscard]] () [[deprecated]]
// (1)~~~~~~~~~~ (2)~~~~~~~~~~~ // (2) seems to have no effect here
{
return 42;
};
Run Code Online (Sandbox Code Playgroud)
这两个列表适用于不同的事物,但我无法弄清楚第二个列表的用途,以及我可以在此处放置哪些属性。对于我在 (2) 中尝试的任何属性,Clang 警告:'...' attribute cannot be applied to types。
标准是这样说的:
... attribute-specifier-seq [位于位置 (2)] 属于相应函数调用运算符或运算符模板的类型。
attribute-specifier-seq [位于位置 (1)] 属于相应的函数调用运算符或运算符模板。
“属于相应函数调用运算符的类型”让我感到困惑。我可以在这里放置哪些属性?这是否仅适用于特定于编译器的属性?如果是,这里可以使用哪些非标准属性?
显然,列表 (2) 早在 C++23 之前就已存在(该提案添加了列表 (1))。
在使用 C++20 的项目中,CLion 建议我添加[[nodiscard]]到我的 const 类方法定义中,例如,
class Test {
public:
[[nodiscard]] int f(int a, int b) const {
return a + b;
}
}
Run Code Online (Sandbox Code Playgroud)
解释是
向成员函数添加 [[nodiscard]] 属性(在 C++17 中引入),以便在编译时突出显示哪些返回值不应被忽略。
我还检查了cppreference.com:
如果从废弃值表达式(而不是强制转换为 void)调用声明为 nodiscard 的函数或按值返回声明为 nodiscard 的枚举或类的函数,则鼓励编译器发出警告。... 出现在函数声明、枚举声明或类声明中。
如果从丢弃值表达式而不是强制转换为 void,
- 调用声明为 nodiscard 的函数,或者
- 调用返回按值声明为 nodiscard 的枚举或类的函数,或者
- 通过显式类型转换或 static_cast 调用声明为 nodiscard 的构造函数,或者
- 声明为 nodiscard 的枚举或类类型的对象通过显式类型转换或 static_cast 进行初始化,
鼓励编译器发出警告。
老实说,我不太明白为什么这个位置需要这个注释。如果调用者进一步处理它们,为什么编译器会忽略我的返回值?是否有一个直观的解释来解释它到底告诉编译器什么以及为什么需要它?
假设我有这门课:
class [[nodiscard]] MyClass
{
public:
MyClass() : _x(x) {}
MyClass(int x) : _x(x) {}
private:
int _x;
};
Run Code Online (Sandbox Code Playgroud)
将标签单独添加[[nodiscard]]到类的构造函数中会改变什么吗?或者这对于[[nodiscard]]应用于类声明本身的标签来说是完全多余的,因此只会给头文件添加不必要的噪音?
(我的测试表明后者,但我可能会错过一些细微差别)
#include <iostream>
struct Empty {}; // empty class
struct W
{
char c[2];
[[no_unique_address]] Empty e1, e2;
};
int main()
{
std::cout << std::boolalpha;
// e1 and e2 cannot have the same address, but one of them can share with
// c[0] and the other with c[1]
std::cout << "sizeof(W) == 2 is " << (sizeof(W) == 2) << '\n';
}
Run Code Online (Sandbox Code Playgroud)
文档说输出可能是:
sizeof(W) == 2 为真
然而,gcc 和 clang 的输出如下:
sizeof(W) == 2 为 false …
现代 C++ 中有[[likely]]和属性。G++ 和 clang++ 中[[unlikely]]都有相应的__builtin_expect(x, 1)内置函数。__builtin_expect(x, 0)但也有__builtin_unpredictable(x)和__builtin_expect_with_probability(x, 1, 0.5)或(同样)__builtin_expect_with_probability(x, 0, 0.5)内置函数,它告诉编译器防止 CPU 用来自(错误)预测分支的指令填充管道,因为从错误预测路径刷新+恢复管道的成本在统计上大于执行 w/o完全是投机执行。
在和分支上使用[[likely]]或同样[[unlikely]]使用属性(如以下代码片段所示)是否等同于使用假设属性?ifelse[[unpredictable]]
if (x) [[likely]] {
// "if" branch
} else [[likely]] {
// "else" branch
}
Run Code Online (Sandbox Code Playgroud)
或者
if (x) [[unlikely]] {
// "if" branch
} else [[unlikely]] {
// "else" branch
}
Run Code Online (Sandbox Code Playgroud)
据我所知,如果存在,if则编译器默认将分支视为默认情况,如果不存在(因为它通常是从当前函数提前退出的不愉快路径检查的形式)。因此,如果我只是省略任何一个属性,那么它并不等同于指定假设属性。[[likely]]else[[unlikely]]else[[unpredictable]]
c++ compiler-optimization speculative-execution branch-prediction c++-attributes
我想编写一个string_to_float带有模板参数的函数,T分别满足、和、when和。我的尝试如下:string_to_float = std::stofstring_to_float = std::stodstring_to_float = std::stoldT = floatT = doubleT = long double
template<typename T>
T string_to_float(std::string const& s, std::size_t* pos = nullptr)
{
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double> || std::is_same_v<T, long double>,
"T is not a built-in floating point type");
if constexpr (std::is_same_v<T, float>)
return std::stof(s, pos);
if constexpr (std::is_same_v<T, double>)
return std::stod(s, pos);
if constexpr (std::is_same_v<T, long double>)
return std::stold(s, pos);
return T{};
}
Run Code Online (Sandbox Code Playgroud)
不过,我对这个return声明感到担忧。虽然在这种情况下静态断言已经失败,但我不想在 …
c++ ×6
c++17 ×2
c++20 ×2
nodiscard ×2
c++23 ×1
if-constexpr ×1
lambda ×1
performance ×1
standards ×1
syntax ×1