小编Oli*_*liv的帖子

为什么const volatile引用不能绑定到右值引用?

我想理解为什么const volatile引用不能绑定到右值引用?禁止这种转换的理由是什么?

在下面的代码中,我注释掉了不编译的行:

int main(){
 int i=1;
 //const volatile int& cvi2=std::move(i); -> ERROR: why?
 const volatile int i2=0;
 //const volatile int& cvi3=std::move(i2);// -> ERROR: why?
}
Run Code Online (Sandbox Code Playgroud)

这是一个更现实的场景,由于类似的原因无法编译:

#include<iostream>
template<class T> void g(const T& b){
  //do usefull things
}
template<class T,class F> void f(T& a,F a_func){
  //do usefull things with a
  a_func(std::move(a));
}
int main(){
   int i=0;
   volatile int vi=1;
   f(i,g<int>); //OK no error;
   f(vi,g<volatile int>);//ERROR: can not convert volatile int to
                                 //const volatile int &
 }
Run Code Online (Sandbox Code Playgroud)

在这段代码中,我希望g<volatile int>(const volatile&) …

c++ c++11

11
推荐指数
2
解决办法
626
查看次数

在哪个线程中调用终止处理程序?

在哪个线程中称为终止处理程序:

  1. noexcept函数内部抛出异常时?

  2. 当用户调用std::terminate()?

  3. 在启动或破坏thread

它是否在标准中定义,我是否可以访问thread_local对象?

c++ terminate terminate-handler

11
推荐指数
1
解决办法
509
查看次数

volatile类型的丢弃值表达式与volatile内置类型的表达式不同

考虑以下这段代码:

struct S{
  int i;
  S(int);
  S(const volatile S&);
  };

struct S_bad{
  int i;
  };

volatile S     as{0};
volatile S_bad as_bad{0};
volatile int   ai{0};

void test(){
   ai;     //(1)=> a load is always performed
   as;     //(2)=> Should call the volatile copy constructor
   as_bad; //(3)=> Should be ill-formed
   }
Run Code Online (Sandbox Code Playgroud)

表达式ai;,as;并且as_bad是废弃的值表达式,并根据C++草案标准N4659/[expr] .12我预计在这三种情况下将应用左值到右值.对于情况(2),这应该导致对volatile复制构造函数(S(const volatile S&))[expr]/12的调用

[...]如果表达式是此可选转换后的prvalue,则应用临时实现转换([conv.rval]).[注意:如果表达式是类类型的左值,则它必须具有易失性复制构造函数来初始化临时值,该临时值是左值到右值转换的结果对象. - 结束说明]

因此案例(3)应该是不正确的.

然而,编译器的行为似乎很混乱:

  1. GCC:

    • ai;=>加载值ai;
    • as; =>没有代码生成,没有警告;
    • as_bad;=>加载as_bad.i …

c++ volatile language-lawyer

11
推荐指数
1
解决办法
307
查看次数

复制elision用于列表初始化,标准中说明了哪些内容?

[dcl.init] /17.6中,明确写出,对于括号初始化的情况,会发生复制省略:

如果初始化表达式是prvalue且源类型的cv-nonqualified版本与目标类相同,则初始化表达式用于初始化目标对象.[  例子: T x = T(T(T())); 调用T默认构造函数来初始化x.-  结束例子  ]

但是在列表初始化的情况下,上面的段落不适用,我没有发现任何类似的东西.见[dcl.init.list].

那么为什么在这种情况下有复制省略:T x{T(T())};根据C++ 17标准.

c++ language-lawyer copy-elision list-initialization c++17

11
推荐指数
1
解决办法
349
查看次数

类模板部分特化等价

两个不同的类模板部分特化声明何时匹配?

在下面的代码中有两个部分特化声明:

  • S<constrain<T,has_accept_>, void>
  • S<constrain<T,has_visit_>, void>

constrain是一个别名模板,它等于T但是使用enable_if第二个参数作为概念的技巧进行约束.

GCC认为这两个部分专业化是不同的,但Clang和MSVC认为它们是等价的,因此拒绝代码:

#include <type_traits>
#include <utility>
using namespace std;

template<class T,class=void>
struct has_accept
  :false_type{};
template<class T>
struct has_accept<T,void_t<decltype(declval<const T&>().accept())>>
  :true_type{};

template<class T,class=void>
struct has_visit
  :false_type{};
template<class T>
struct has_visit<T,void_t<decltype(declval<const T&>().visit())>>
  :true_type{};

//pre c++17 clang/MSVC fix: default argument of template 
//   used as template template argument not implemented yet
template<class T> using has_accept_ = has_accept<T>;
template<class T> using has_visit_ = has_visit<T>;

template<class T,template<class> class TT,class=enable_if_t<TT<T>::value>>
using …
Run Code Online (Sandbox Code Playgroud)

c++ language-lawyer c++17

11
推荐指数
1
解决办法
177
查看次数

使用std :: launder从指向非活动对象的指针获取指向活动对象成员的指针?

这个问题followes这一个

我们来考虑这个示例代码:

struct sso
  {
  union{
    struct {
      char* ptr;
      char size_r[8];
      } large_str;
    char short_str[16];
    };

  bool is_short_str() const{
    return *std::launder(short_str+15)=='\0'; //UB?
    }
  };
Run Code Online (Sandbox Code Playgroud)

如果short_str不是取消引用指针的活动成员而std::launder不是UB.让我们考虑ABI已经明确指定,并且我们知道size_r [7]与short_str [15]位于同一地址.并std::launder(short_str+15)返回一个指针size_r[7]的时候short_str是不是工会的活跃成员?


Nota:我认为情况就是这样,因为[ptr.launder]/3

如果对象Y位于Y所占用的存储区内,则指向存储的字节可以到达,如果Y是指针可互换的对象,则指向对象Y,或者如果Y是数组元素,则指向立即封闭的数组对象.

c++ pointers unions language-lawyer c++17

10
推荐指数
1
解决办法
456
查看次数

应该通过类模板特化推导来考虑推导指导参数初始化吗?

作为这个问题的后续,我测试了clang和gcc的行为.似乎两个编译器对c ++标准有不同的解释.

在下面的示例中,如果需要根据推理指南假设的构造函数参数复制不可复制的参数,则GCC拒绝编译.Clang不执行此检查:

#include <cstddef>

struct not_copyable{
    not_copyable()=default;
    not_copyable(const not_copyable&)=delete;
};
struct movable{
    movable()=default;
    movable(movable&&);
};

template <typename T, size_t N>
struct A
 { template <typename ... Ts> A (Ts const & ...) {} };

template <typename T, size_t N>
struct B
 { template <typename ... Ts> B (const Ts & ...) {} };

template <typename T, typename ... Ts>
A(T const &, Ts const & ...) -> A<T, 1U + sizeof...(Ts)>;

template <typename T, typename ... Ts> …
Run Code Online (Sandbox Code Playgroud)

c++ language-lawyer template-argument-deduction c++17

10
推荐指数
1
解决办法
257
查看次数

对象的生命周期,在哪种情况下重用存储?

在C++ ISO标准N4618中(但它几乎也适用于C++ 11版本),可以阅读:

在§1.8C++对象模型:

如果在与e"N unsigned char数组"类型的另一个对象关联的存储中创建了一个完整对象(5.3.4),该数组为创建的对象提供存储... [注意:如果该数组的那部分先前提供了存储对于另一个对象,该对象的生命周期因为其存储被重用而结束]

=>好的,unsigned char数组可以为其他对象提供存储空间.如果新对象占用了之前被其他对象占用的存储空间,则新对象会重用前一个对象的存储空间.

在§3.8.8对象生命周期

如果在对象的生命周期结束之后并且在重用或释放对象占用的存储之前,则在原始对象占用的存储位置处创建新对象,...

=>我可以在另一个对象的存储位置构造一个对象,但是这个操作不是"存储重用"(否则为什么要写入...在对象占用的存储被重用之前......)

并作为§3.8.8的一个例子

struct C {
 int i;
 void f();
 const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();          // lifetime of *this ends
    new (this) C(other); // new object of type C created
    f();                 // well-defined
  }
  return *this;
}

C c1;
C c2;
c1 …
Run Code Online (Sandbox Code Playgroud)

c++ language-lawyer c++11 c++17

9
推荐指数
1
解决办法
550
查看次数

使用std :: launder获取指向活动联合成员的指针,而不是指向非活动联合成员的指针?

考虑这个联合:

union A{
  int a;
  struct{
    int b;
    } c;
  };
Run Code Online (Sandbox Code Playgroud)

c并且a不是布局兼容类型,因此无法读取bthrough 的值a

A x;
x.c.b=10;
x.a+x.a; //undefined behaviour (UB)
Run Code Online (Sandbox Code Playgroud)

对于审判1和审判2,请参阅此问题

试用3

现在让我们使用std::launder它似乎不想要的东西:

A x;
x.a=10;
auto p = &x.a;                 //(1)
x.c.b=12;                      //(2)
p = std::launder(p);           //(2')
*p+*p;                         //(3)  UB?
Run Code Online (Sandbox Code Playgroud)

可以std::launder改变什么吗?根据[ptr.launder]

template <class T> constexpr T* launder(T* p) noexcept;

需要p表示内存中一个字节的地址A。在其生存期内且类型相似的对象XT位于地址A处。通过结果可以访问的所有存储字节都是可以访问的p(请参见下文)。

返回: …

c++ pointers undefined-behavior language-lawyer c++17

8
推荐指数
1
解决办法
270
查看次数

"洗衣"是否通过指针算法传播?

P0532R0中所述,在下面的用例中std::launder必须使用以避免未定义的行为(UB):

struct X{
  const int i;
  x(int i):i{i}{}
  };

unsigned char buff[64];
auto p = new(buff) X(33);
p->~X();
new(buff) X(42);
p = std::launder(p);
assert(p->i==42);
Run Code Online (Sandbox Code Playgroud)

但是在缓冲区中有多个对象的情况下会发生什么(如果X在向量中推送2 ,则会发生这种情况,清除向量然后再推送两个对象X):

unsigned char buff[64];
auto p0 = new(buff) X(33);
auto p1 = new(p0+1) X(34);
p1->~X();
p0->~X();
new(buff) X(42);
new(p0+1) X(43);
p0 = std::launder(p0);
assert(p0->i==42);
assert(p0[1].i==43);//???
Run Code Online (Sandbox Code Playgroud)

最后一个断言是正确的,还是p0[1]仍然调用UB?

c++ undefined-behavior c++17

7
推荐指数
1
解决办法
422
查看次数