std::is_function 专门用于具有类似于以下特征的类型的类型:
int(int) &
Run Code Online (Sandbox Code Playgroud)
看到这里:std :: is_function
但这既不是指向成员方法的指针,哪个签名可能是:
int(T::*)(int) &
Run Code Online (Sandbox Code Playgroud)
它也不是对函数的引用:
int (&)(int)
Run Code Online (Sandbox Code Playgroud)
那个奇怪的签名是什么?
C++标准指定mutex,atomics或conditinal_variable是标准布局类型.
这个规范有什么好处?用户如何利用此属性?
总的来说,如果知道一个类型是标准布局而不知道其实现的细节,我可以获得什么?
我试图了解重载决议.
首先让我们考虑第一种情况:
struct int1{
int val;
operator int&()&{
return val;
}
operator const int &() const&{
return val;
}
};
void f(int &){} //f#1
void f(const int&){} //f#2
void test1(){
int1 x;
f(x);
//Conversion sequence for f#1:
// - int_wrapper& --> int1::operator int&
// => Ranking: user defined conversion rank
//Converison sequence for f#2:
// - int1& --> int1::operator int & --> const int&
// - int1& --> const int1 & --> int1::operator const int&
// => Ranking: …Run Code Online (Sandbox Code Playgroud) 考虑以下两个类:
class B
{
public:
B() { }
B(const B& b) = delete; //Move ctor not implicitly declared
};
class A
{
public:
A() { }
operator B()
{
return B();
}
};
Run Code Online (Sandbox Code Playgroud)
我可以看到为什么这段代码编译得很好:
A a;
B b = a;
Run Code Online (Sandbox Code Playgroud)
遵循复制初始化的规则,对象"a"被转换为类型B的prvalue,因为在C++ 17中不再需要复制构造函数,所以没有错误:
如果T是类类型,并且其他类型的cv-nonqualified版本不是T或从T派生,或者如果T是非类型类型,但是other的类型是类类型,则是用户定义的转换序列可以检查从其他类型转换为T(或从T派生的类型,如果T是类类型并且转换函数可用),并通过重载决策选择最佳的类型.转换的结果,即prvalue临时(直到C++ 17)prvalue表达式(自C++ 17),如果使用转换构造函数,则用于直接初始化对象.最后一步通常是优化的,转换的结果直接在为目标对象分配的内存中构造,但是即使没有使用,也需要访问适当的构造函数(移动或复制).(直到C++ 17)
但是为什么这个直接列表初始化编译呢?
A a;
B b{ a };
Run Code Online (Sandbox Code Playgroud)
我在列表初始化中找不到任何措辞,说明编译器在这种情况下应该尝试将A转换为B. 只考虑构造函数的重载决策:
如果前一阶段没有产生匹配,则T的所有构造函数都参与对由braced-init-list元素组成的参数集的重载解析,并限制只允许非缩小转换
但是在这种情况下,复制构造函数被删除,所以不应该通过重载决策来选择它吗?
如果我将operator delete定义为如下,并且如果对象构造函数抛出新表达式,我希望看到调用定义的运算符的结果delete:
#include <new>
#include <cstdlib>
#include <iostream>
void*
operator new(std::size_t s){
std::cout << "alloc " << std::endl;
return std::malloc(s);
}
void
operator delete(void* p) noexcept {
std::cout << "dealloc " << std::endl;
std::free(p);
}
void
operator delete(void* p,std::size_t) noexcept{
std::free(p);
std::cout << "dealloc s" << std::endl;
}
struct A{
A(int i){
if(i>0)
throw 10;
}
};
int main(int argc// will equal 10
,char* arg[])
{
for(int i=0;i<argc;++i)
auto p=new A{argc};
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这个程序刚输出alloc,为什么不调用operator delete?在标准[expr.new]中指定:
如果上述对象初始化的任何部分通过抛出异常终止并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续在上下文中传播.新表达的.
GCC 7.2和Clang 5.0在这种情况下不一致:
struct A;
A foo();
struct A
{
static void bar()
{
foo();
}
private:
~A()=default;
};
A foo()
{
return {};
//GCC error: A::~A() is private in this context.
};
Run Code Online (Sandbox Code Playgroud)
此行为是"c ++ 17保证(复制省略并且与RVO或NRVO无关)"的一部分.
GCC不会编译此代码,但Clang会这样做.哪一个错了?
也许这段话说机器人Clang和GCC符合标准[class.temporary]:
当类类型X的对象传递给函数或从函数返回时,如果X的每个复制构造函数,移动构造函数和析构函数都是微不足道的或删除的,并且X至少有一个未删除的复制或移动构造函数,则实现是允许创建一个临时对象来保存函数参数或结果对象.临时对象分别由函数参数或返回值构造,并且函数的参数或返回对象被初始化,就好像通过使用未删除的普通构造函数来复制临时对象(即使该构造函数不可访问或不会被选中)通过重载决策来执行对象的复制或移动).[注意:允许此纬度允许类类型的对象传递给寄存器中的函数或从寄存器中的函数返回. - 结束说明]
我无法找到标准中的哪个地方声明禁止使用volatile&或const volatile&参数显式默认复制构造函数和复制赋值,如下所示:
struct A{
A(const volatile A&) =default; // fails to compile on (all) compilers
};
Run Code Online (Sandbox Code Playgroud)
在[dcl.fct.def.default]中没有这样的限制,而[class.copy]指定它A(const volatile A&)是一个复制构造函数.
注意:我只是在标准文本中查找指定此行为的位置.
在C++标准[temp.over.link]中,解释了函数模板等价性的确定不应涉及编译器的“英勇努力”。
例如,C++ 标准提出了以下建议:
// guaranteed to be the same
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);
// guaranteed to be different
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);
// ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);
Run Code Online (Sandbox Code Playgroud)
此规则是否也适用于涉及元编程的情况,如下例所示?
template<class T>
struct t_{
using type = T;
};
//ill-formed or different?
template<class T> T f(T);
template<class T> typename t_<T>::type f(T);
Run Code Online (Sandbox Code Playgroud) 在c ++标准中,在[basic.lval] /11.6中说:
如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[...]
- 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),[...]
这句话是严格别名规则的一部分.
它可以允许我们访问非现有联盟的非活动成员吗?如:
struct A{
int id :1;
int value :32;
};
struct Id{
int id :1;
};
union X{
A a;
Id id_;
};
void test(){
A a;
auto id = reinterpret_cast<X&>(a).id_; //UB or not?
}
Run Code Online (Sandbox Code Playgroud)
注意:对标准中我没有掌握的内容进行解释,以及为什么上面的例子可能有用.
我想知道[basic.lval] /11.6有什么用处.
[class.mfct.non-static]/2禁止我们调用"已转换为"联合或聚合的成员函数:
如果为非X类型的对象或从X派生的类型调用类X的非静态成员函数,则行为未定义.
考虑到静态数据成员访问,或静态成员函数可以直接使用qualified-name(a_class::a_static_member)执行,唯一有用的用例[basic.lval] /11.6,可能是访问"casted to"union的成员.我想过使用这个最后的标准规则来实现"优化变体".此变体可以包含A类对象或B类对象,这两个对象以大小为1的位域开头,表示类型:
class A{
unsigned type_id_ :1;
int value :31;
public:
A():type_id_{0}{}
void bar{};
void baz{};
};
class B{
unsigned type_id_ :1;
int value …Run Code Online (Sandbox Code Playgroud) 根据标准,协程只有在挂起时才会被销毁[dcl.fct.def.coroutine]
如果为未挂起的协程调用 destroy,则程序具有未定义的行为。
在协程 A 内评估等待表达式期间,假设此事件序列对应于[expr.await]/5.1:
协程 A 可以在协程 B 恢复之后、挂起之前被销毁吗?
示例代码:
#include <coroutine>
using namespace std;
struct task
{
struct promise_type;
using handle_type = coroutine_handle<promise_type>;
struct promise_type
{
handle_type resumer = nullptr;
auto
get_return_object(){
return task{handle_type::from_promise(*this)};
}
auto
initial_suspend(){
return suspend_always {};
}
auto
unhandled_exception(){}
auto
final_suspend(){
return suspend_always{};
}
void
return_void() {}
};
handle_type handle;
void await_resume(){
handle.resume();
}
auto
await_suspend(handle_type …Run Code Online (Sandbox Code Playgroud) c++ ×10
c++17 ×2
atomic ×1
c++20 ×1
coroutine ×1
declaration ×1
mutex ×1
new-operator ×1
overloading ×1
templates ×1
temporary ×1
types ×1
unions ×1
volatile ×1