小编Oli*_*liv的帖子

自C++ 17以来,具有正确地址和类型的指针仍然始终是有效指针吗?

(参考这个问题和答案.)

在C++ 17标准之前,[basic.compound]/3中包含以下句子:

如果类型T的对象位于地址A,则类型为cv T*的指针(其值为地址A)被称为指向该对象,而不管该值是如何获得的.

但是自从C++ 17以来,这句话已被删除.

例如,我相信这句话使这个示例代码定义,并且从C++ 17开始这是未定义的行为:

 alignas(int) unsigned char buffer[2*sizeof(int)];
 auto p1=new(buffer) int{};
 auto p2=new(p1+1) int{};
 *(p1+1)=10;
Run Code Online (Sandbox Code Playgroud)

在C++ 17之前,p1+1保持地址*p2并具有正确的类型,因此*(p1+1)是指向*p2.在C++中,17 p1+1是一个指向前端指针,所以它不是指向对象指针,我相信它不是可以解除引用的.

对标准权的这种修改的解释是否还有其他规则来补偿所引用的句子的删除?

c++ pointers language-lawyer c++14 c++17

80
推荐指数
2
解决办法
4177
查看次数

限制指针算术或比较的基本原理是什么?

在C/C++中,仅当结果指针位于原始指向的完整对象内时,才定义指针的加法或减法.此外,只有当两个指向对象是唯一完整对象的子对象时,才能执行两个指针的比较.

这种限制的原因是什么?

我认为分段内存模型(参见这里 §1.2.1)可能是其中一个原因,但由于编译器实际上可以定义所有指针的总顺序,如本答案所示,我对此表示怀疑.

c c++ pointers pointer-arithmetic language-lawyer

26
推荐指数
3
解决办法
1195
查看次数

对const c-array副本的结构化绑定是const吗?

考虑这段代码(演示):

#include <tuple>
#include <type_traits>

struct Ag{int i;int j;};
using  T = std::tuple<int,int>;
using  Ar = int[2];

const Ag ag {};
const T t   {};
const Ar ar {};

void bind_ag(){
    auto [i,j] = ag;
    static_assert(std::is_same_v<decltype((i)),int&>);
    }
void bind_t(){
    auto [i,j] = t;
    static_assert(std::is_same_v<decltype((i)),int&>);
    }
void bind_ar(){
    auto [i,j] = ar;
    static_assert(std::is_same_v<decltype((i)),int&>);       //For GCC
    static_assert(std::is_same_v<decltype((i)),const int&>); //For Clang (and standard?)
    }
Run Code Online (Sandbox Code Playgroud)

结构化结合到一个副本const的c-阵列被声明常量由锵和非const由GCC.

GCC对c阵列的行为与聚合或类似元组类型的行为一致.

另一方面,从我对标准的阅读中,我认为Clang遵循所写的内容.在[dcl.struct.bind]/1中, e具有类型cv A,其中A是初始化表达式的类型,cv是结构化绑定声明的cv限定符.并且初始化表达式的类型 …

c++ language-lawyer c++17 structured-bindings

26
推荐指数
1
解决办法
723
查看次数

从C++ 17开始,使用std :: launder"验证"非"指向对象的指针"指针值

根据这个答案,从C++ 17开始,即使指针具有正确的地址,并且正确的类型解除引用它也会导致未定义的行为.

 alignas(int) unsigned char buffer[2*sizeof(int)];
 auto p1=new(buffer) int{};
 auto p2=new(p1+1) int{};
 *(p1+1)=10; // UB since c++17
Run Code Online (Sandbox Code Playgroud)

原因是指针值p1+1一个指针过去的对象.可以使用以下示例将此示例恢复为已定义的行为std::launder:

 *std::launder(p1+1)=10; // still UB?  
Run Code Online (Sandbox Code Playgroud)

其次,在下列情况下它是否也有用?

alignas(int) unsigned char buffer[3*sizeof(int)];
auto pi = new (buffer) int{};
auto pc = reinterpret_cast<unsigned char*>(pi);//not a "pointer to" an element of buffer 
                                               //since buffer[0] and *pc 
                                               //are not pointer interconvertible
//pc+2*sizeof(int) would be UB
auto pc_valid = std::launder(pc) //pc_valid is a pointer to an element of buffer …
Run Code Online (Sandbox Code Playgroud)

c++ pointers c++17

19
推荐指数
1
解决办法
608
查看次数

概念评估能否取决于评估的位置?

[temp.concept]/5说:

概念未实例化([temp.spec]).[注意:表示概念特化的id表达式被计算为表达式([expr.prim.id]).[...]

这是否意味着此规则([temp.point]/8)不适用?

如果两个不同的实例化点根据单定义规则给出模板特化的不同含义,则程序形成错误,不需要诊断.


例如,如果此规则不适用,则以下代码形式良好:

template<class T>
concept Complete = sizeof(T)==sizeof(T);

struct A;

constexpr inline bool b1 = Complete<A>; //Complete<A>==false;

struct A{};

constexpr inline bool b2 = Complete<A>; //Complete<A>==true;
Run Code Online (Sandbox Code Playgroud)

这个问题是后面这一个

c++ language-lawyer c++-concepts c++20

16
推荐指数
1
解决办法
244
查看次数

复制elision直接基类初始化?

由于构造函数中的B基类子项目的复制构造,以下代码无法使用Gcc和Clang进行编译A:

struct B{
  B();
  B(const B&) =delete;
  };

struct A:B{
  A():B(B()){} //=> error: use of deleted function...
  };
Run Code Online (Sandbox Code Playgroud)

不过根据[class.base.init]/7:

表达式列表支撑-INIT列表MEM-初始化是根据[dcl.init]用于初始化规则用来初始化指定子对象(或者,在一个委派构造,完整的类对象的情况下)直接初始化.

因此初始化规则对于成员或直接基础是相同的.对于成员子对象,Gcc和Clang不使用已删除的复制构造函数:

struct A2:B{
  B b;
  A2():b(B()){} //=> OK the result object of B() is b
  };
Run Code Online (Sandbox Code Playgroud)

这不是Clang和Gcc的编译器错误吗?是不是B应该省略复制构造函数A()


有趣的是,即使gcc检查复制结构是否格式正确,它也会忽略此复制构造函数调用,请参阅此处的程序集

c++ language-lawyer c++17

16
推荐指数
1
解决办法
332
查看次数

自 C++20 以来,是否允许对分配的存储进行指针运算?

在 C++20 标准中,说数组类型是隐式生命周期类型

这是否意味着可以隐式创建非隐式生命周期类型的数组?这种数组的隐式创建不会导致数组元素的创建?

考虑这个案例:

//implicit creation of an array of std::string 
//but not the std::string elements:
void * ptr = operator new(sizeof (std::string) * 10);
//use launder to get a "pointer to object" (which object?)
std::string * sptr = std::launder(static_cast<std::string*>(ptr));
//pointer arithmetic on not created array elements well defined?
new (sptr+1) std::string("second element");
Run Code Online (Sandbox Code Playgroud)

从 C++20 开始,这段代码不再是 UB 了吗?


也许这种方式更好?

//implicit creation of an array of std::string 
//but not the std::string elements:
void * ptr = operator new(sizeof …
Run Code Online (Sandbox Code Playgroud)

c++ arrays lifetime dynamic-arrays c++20

13
推荐指数
1
解决办法
339
查看次数

指针算术是否在联合UB的非活动成员上?

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

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

    const char* get_tag_ptr() const {
        return short_str+15;
    }
};
Run Code Online (Sandbox Code Playgroud)

[basic.expr]中指定只要结果指向数组的另一个元素(或超过对象的末尾或最后一个元素),就允许指针运算.尽管如此,如果数组是联合的非活动成员,则在本节中未指定会发生什么.我相信这不是问题short_str+15,永远不是UB.这样对吗?

以下问题清楚地表明了我的意图

c++ pointer-arithmetic language-lawyer c++11

12
推荐指数
1
解决办法
462
查看次数

我们可以访问不存在的类类型对象的成员吗?

在c ++标准中,在[basic.lval] /11.6中说:

如果程序试图通过以下类型之一以外的glvalue访问对象的存储值,则行为未定义:[...]

  • 聚合或联合类型,包括其元素或非静态数据成员中的上述类型之一(包括递归地,子聚合或包含联合的元素或非静态数据成员),[...]

这句话是严格别名规则的一部分.

它是否允许我们通过类成员访问进行别名?

class A{ //"casted to" aggregate
  int i;
  float g;
  };

int j=10;
int l = reinterpret_cast<A*>(&j)->i;
Run Code Online (Sandbox Code Playgroud)

根据标准,只有在对象受到左值到右值转换 [conv.lval]/2的情况下才能访问对象值.

[expr.ref]没有规定object-expression必须引用该类型的对象,只是glvalue必须具有类类型(object-expression是点"."左侧的表达式).然而,有一句话反复出现在与成员访问相关的句子中,这可能意味着对我忽略的程序的限制.例如[expr.ref] /4.1:

如果E2是静态数据成员且E2的类型是T,则E1.E2是左值; 表达式指定类的命名成员.

"指定"是否意味着该成员在其生命周期内并且我不能使用类成员访问来进行伪造别名?或者[basic.lval] /11.6允许它?

c++ strict-aliasing language-lawyer class-members

12
推荐指数
1
解决办法
370
查看次数

复制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
查看次数