在您尝试阅读整篇文章之前,请了解:
fameta::counter类中,以解决一些剩余的怪癖。你可以在 github 上找到它;自从 Filip Roséen 在 2015 年发现/发明了通过友元注入编译时间计数器的黑魔法是在 C++ 中,我一直对这个设备有点着迷,所以当 CWG决定功能必须去时,我很失望,但仍然充满希望可以通过向他们展示一些引人注目的用例来改变他们的想法。
然后,几年前,我决定再看一遍,以便uberswitch es可以嵌套 - 在我看来,这是一个有趣的用例 - 只是发现它不再适用于新版本的可用的编译器,即使问题 2118是(现在仍然是)处于打开状态:代码会编译,但计数器不会增加。
该问题已在 Roséen 的网站和最近的 stackoverflow 上报告:Does C++ support compile-time counters?
我想了解编译器发生了什么变化,使看似仍然有效的 C++ 不再起作用。为此,我在互联网上广泛搜索,寻找有人谈论它,但无济于事。所以我开始尝试并得出了一些结论,我在这里提出这些结论是希望能从这里的知识渊博的人那里得到反馈。
为了清楚起见,我在下面展示了 Roséen 的原始代码。有关其工作原理的说明,请参阅他的网站:
template<int N>
struct flag {
friend constexpr int adl_flag …Run Code Online (Sandbox Code Playgroud) c++ counter friend-function argument-dependent-lookup constexpr
12.4.2的C++标准规定了这一点
[...]不得采用析构函数的地址.[...]
但是,编译器可以在没有任何抱怨的情况下获取类析构函数周围的包装器的地址,如下所示:
struct Test {
~Test(){};
void destructor(){
this->~Test();
}
};
void (Test::*d)() = &Test::destructor;
Run Code Online (Sandbox Code Playgroud)
那么禁止直接获取析构函数地址的理由是什么呢?
在 c++20 中玩弄文字、非类型模板参数,我发现 g++ 和 clang++ 不同意以下代码。
#include <algorithm>
template<size_t N>
struct StringLiteral {
constexpr StringLiteral(const char (&str)[N]) {
std::copy_n(str, N, value);
}
char value[N];
};
template <typename T, StringLiteral Name>
struct named{};
template <typename T>
struct is_named: std::false_type{};
template <typename T, size_t N, StringLiteral<N> Name>
struct is_named<named<T, Name>>: std::true_type{};
// This will fail with g++
static_assert(is_named<named<int, "ciao">>::value == true);
Run Code Online (Sandbox Code Playgroud)
在 Godbolt 上实时查看:https ://godbolt.org/z/f3afjd
首先也是最重要的,我什至不确定我是否以正确的方式做这件事:它是与泛型StringLiteral<N>类型匹配的方式,还是不是?如果不是,正确的方法是什么?
而且,为什么编译器不同意呢?谁有错误?
编辑:发现删除size_t N部分特化中的参数使两个编译器都同意,结果是预期的。像这样:
template <typename T, StringLiteral …Run Code Online (Sandbox Code Playgroud) 我想测试一个临时对象是否至少与持有const引用的临时对象一样长,所以我想出了这个例子.
#include <iostream>
struct Test {
Test() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
~Test() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
struct Holder {
Holder(const Test& t):m_t(t) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
~Holder() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
const Test& m_t;
};
int main() {
Holder(Test());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但是,我很惊讶地看到编译器实际上已经优化了整个事情,就像在codebolt上看到的那样
但是,如果我真的给临时的名字,通过改变线
Holder(Test());
Run Code Online (Sandbox Code Playgroud)
成
Holder h((Test()));
Run Code Online (Sandbox Code Playgroud)
然后它"神奇地"起作用:codebolt.
情节扭曲:如果我切换到c ++ 11并使用类的支撑列表初始化Holder,那么无论我是否为临时名称赋予构造函数,都不会省略构造函数.见codebolt一次.
那么这里有什么问题?我的印象是,带有副作用的构造函数永远不会被省略,但我显然错过了版本之间变化的标准的重要部分. …
我一直都被教导过
1. Class c(arg);
Run Code Online (Sandbox Code Playgroud)
和
2. Class c = arg;
Run Code Online (Sandbox Code Playgroud)
是两个完全相同的陈述,但看看这种情况.
#include <iostream>
class Intermediary {
};
class Left {
public:
Left(const Intermediary &) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
class Right {
public:
// The argument is there just so that the example can work, see below
Right(int) {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
operator Intermediary () const {
std::cout << __PRETTY_FUNCTION__ << std::endl;
return Intermediary();
}
};
Run Code Online (Sandbox Code Playgroud)
现在,如果我这样做:
Left l = Right(0);
Run Code Online (Sandbox Code Playgroud)
编译器会抱怨
error: conversion …Run Code Online (Sandbox Code Playgroud) 在我的 Windows 应用程序中,我WaitForSingleObject()在从控制台读取时使用:
HANDLE h = (void*)GetStdHandle(STD_INPUT_HANDLE);
char buf[512] = { 0 };
DWORD r = 0;
if (WaitForSingleObject(h, 3000) == WAIT_OBJECT_0)
{
ReadConsole(h, buf, 512, &r, 0);
}
Run Code Online (Sandbox Code Playgroud)
看起来,为了将上面的代码移植到 Linux,我必须实现一个条件等待。我将如何实施?在读取某些字符之前,如何检查它们是否已写入控制台?