小编Lin*_*gxi的帖子

为什么将整数文字强制转换为指针值会导致非const表达式?

我正在尝试编写一个结构来计算基类和派生类之间的指针偏移量,作为C++ 03中的常量表达式.代码如下:

template <typename Base, typename Derived, typename Via = Derived>
struct OffsetToBase
{
    static const std::ptrdiff_t val =
        (const char*const)static_cast<Base*const>(static_cast<Via*const>((Derived*const)(1u << 7))) -
        (const char*const)(1u << 7);
};
Run Code Online (Sandbox Code Playgroud)

代码在GCC中编译,但不在clang和VC中编译.clang和VC产生的错误基本上表明初始化器不是一个常量表达式,clang进一步强调了子表达式(Derived*const)(1u << 7).

所以,我的问题是标准对此有何看法?如果初始化程序根据标准不符合常量表达式,那么这背后的原因是什么?

更新: 为了您的兴趣,我发现了以下两个讨论:

"初始化器元素不是常量"错误在Linux GCC中没有任何理由,编译C

关于转换为整数常量表达式(在标准C中)

但我不认为相同的规则适用于C++.

c++ expression casting const

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

围绕功能调用解决方案的混乱

这个问题的灵感来自于这个问题.考虑一下代码:

namespace ns {
  template <typename T>
  void swap(T& a, T& b) {
    using namespace std;
    swap(a, b);
  }
}
Run Code Online (Sandbox Code Playgroud)

在使用GCC进行一些测试后,我发现swap(a, b);解析为
1)std::swap如果T已经过载std::swap(例如,标准容器类型)
2)ns::swap否则,导致无限递归.
因此,似乎编译器将首先尝试在命名空间中找到匹配项ns.如果找到匹配项,则搜索结束.但是当ADL进入时并非如此,在这种情况下,std::swap无论如何都会找到.解决过程似乎很复杂.

我想知道swap(a, b)在上面的上下文中解析函数调用过程中发生了什么的细节.可以参考该标准.

c++ function-calls language-lawyer overload-resolution argument-dependent-lookup

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

签名std :: strtol

签名的std::strtol表述如下:

long strtol(const char *str, char **str_end, int base);

我实际上期望第二个参数是类型const char**.是char**什么类型的解释?请注意,可以修改类型的变量const char**.你无法修改的是一个char* const *变量.

c++

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

为什么std :: vector :: max_size()是非静态的?

由于最大大小不依赖于特定std::vector对象,我想它应该是一个静态方法.但是,标准原型仍然是非静态的,我无法弄清楚原因.从逻辑上讲,应该在创建任何std::vector对象之前检查最大大小.

请注意,这个问题与基本上不同constexpr.

c++ library-design c++-standard-library language-lawyer

7
推荐指数
0
解决办法
78
查看次数

为什么通用代码编译而不约束T?

以下代码片段是从C#5.0编程的例10-11(p.343)中提取的:

public static T[] Select<T>(this CultureInfo[] cultures,
                            Func<CultureInfo, T> map)
{
    var result = new T[cultures.Length];
    for (int i = 0; i < cultures.Length; ++i)
    {
        result[i] = map(cultures[i]);
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

我无法弄清楚如何在不T通过对其应用约束来暴露任何信息的情况下编译它.具体来说,编译器如何知道为数组分配多少字节,因为它T可能不是引用类型,而是值类型(即a struct)?此外,赋值操作的语义result[i] = map(cultures[i])似乎取决于T引用类型还是值类型.

c# generics

7
推荐指数
2
解决办法
108
查看次数

是否可以删除未使用新表达式构造的对象?

小心翼翼,这可能不太好.根据cppref:

如果expression是其他任何东西,包括它是否是由new-expression的数组形式获得的指针,则行为是未定义的.

把它放在一边,实际上是下面的代码OK(T非数组,假设new没有替换)?

auto p = (T*)operator new(sizeof(T));
new(p) T{};
delete p;
Run Code Online (Sandbox Code Playgroud)

据说在cppref

当调用分配功能,新的表达通过请求作为第一个参数,类型的字节数 std::size_t,而这正是sizeof(T)对于非数组T.

所以我想这可能还行.但是,据说从C++ 14开始,

允许新表达式忽略或组合通过可替换分配函数进行的分配.在省略的情况下,存储器可以由编译器提供而不需要调用分配函数(这也允许优化掉未使用的new-expression).在组合的情况下,如果满足以下所有条件,则可以扩展由新表达式E1进行的分配以为另一个新表达式E2提供额外的存储:[...]

请注意,只有在使用new-expression时才允许此优化,而不是任何其他方法来调用可替换的分配函数: delete[] new int[10];可以优化,但operator delete(operator new(10));不能.

我不太确定其含义.那么,这在C++ 14中可以吗?

我为什么问这个问题?(来源)

有时,内存分配和初始化无法在一个步骤中完成.您必须手动分配内存,执行其他操作,然后初始化对象,例如,以提供强大的异常安全性.在这种情况下,如果删除表达式无法在结果指针上使用,则必须手动取消初始化和取消分配,这很乏味.更糟糕的是,如果使用新表达式和手动方法,则必须跟踪每个对象使用哪一个.

c++ new-operator dynamic-memory-allocation delete-operator c++14

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

初始化对象时是否可以放弃放置新的返回值

这个问题源于这个帖子中的评论部分,并且在那里也得到了答案.但是,我认为仅仅留在评论部分是非常重要的.所以我为此做了这个Q&A.

Placement new可用于初始化分配的存储中的对象,例如,

using vec_t = std::vector<int>;
auto p = (vec_t*)operator new(sizeof(vec_t));
new(p) vec_t{1, 2, 3}; // initialize a vec_t at p
Run Code Online (Sandbox Code Playgroud)

根据cppref,

安置新的

如果提供了placement_params,则将它们作为附加参数传递给分配函数.在标准分配函数之后,这种分配函数被称为"placement new" void* operator new(std::size_t, void*),它只是简单地返回其第二个参数.这用于在分配的存储中构造对象[...]

这意味着new(p) vec_t{1, 2, 3}简单地返回p,并且p = new(p) vec_t{1, 2, 3}看起来多余.忽略返回值真的可以吗?

c++ initialization placement-new compiler-optimization

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

在新表达式中进行内存分配后是否对初始化程序进

考虑一下代码

auto p = new T( U(std::move(v)) );
Run Code Online (Sandbox Code Playgroud)

然后是初始化器U(std::move(v)).让我们假设T( U(std::move(v)) )不抛出.如果在底层内存分配之后评估初始化程序,则代码是强异常安全的.否则,事实并非如此.如果抛出内存分配,v就已经被移动了.因此,我对内存分配和初始化程序评估之间的相对顺序感兴趣.它是定义的,未指定的,还是什么?

c++ exception new-operator dynamic-memory-allocation object-initialization

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

内置数组的大小是什么类型的?

例如,在推导非类型模板参数时,必须在参数和参数之间精确匹配n类型n.所以下面的代码不会编译(至少在GCC和clang上):

#include <array>
template <int n> void f(std::array<int, n> const&) {}
int main() {
  std::array<int, 3> arr;
  f(arr);
}
Run Code Online (Sandbox Code Playgroud)

这是因为std::array声明为

template <typename T, std::size_t n> class array;
Run Code Online (Sandbox Code Playgroud)

而不是

template <typename T, int n> class array;
Run Code Online (Sandbox Code Playgroud)

但是,要捕获内置数组的大小,似乎任何整数类型都可以.以下是关于GCC,clang和VC++的所有工作:

template <typename T, char n > void f(T (&)[n]);
template <typename T, short n> void f(T (&)[n]);
template <typename T, int   n> void f(T (&)[n]);
...
Run Code Online (Sandbox Code Playgroud)

那么,严重的是,内置数组的大小类型是否超载?

c++ arrays templates

6
推荐指数
2
解决办法
375
查看次数

在Windows上引用MapViewOfFile的计数问题

似乎MapViewOfFile增加了文件映射内核对象的引用计数.

引自MSDN描述MapViewOfFile:

文件映射对象的映射视图维护对象的内部引用,并且文件映射对象在释放对它的所有引用之前不会关闭.因此,要完全关闭文件映射对象,应用程序必须通过调用UnmapViewOfFile并通过调用并关闭文件映射对象句柄来取消映射文件映射对象的所有映射视图CloseHandle.可以按任何顺序调用这些函数.

此外,从Windows到C/C++,第5版:

上面的代码显示了用于操作内存映射文件的"预期"方法.但是,它没有显示的是,当您调用时,系统会增加文件对象和文件映射对象的使用计数MapViewOfFile...

尽管如此,我的实际测试表明相反.我在Windows 10 64位上使用Visual Studio 2015.测试程序如下:

#include <windows.h>

int main() {
  HANDLE h = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 128, "test");
  void* p_memory = MapViewOfFile(h, FILE_MAP_WRITE, 0, 0, 0);
  CloseHandle(h);
  h = OpenFileMappingA(FILE_MAP_WRITE, FALSE, "test");
  DWORD dw = GetLastError(); // ERROR_FILE_NOT_FOUND
}
Run Code Online (Sandbox Code Playgroud)

OpenFileMapping呼叫因上一个错误而失败ERROR_FILE_NOT_FOUND.当我取消CloseHandle通话时,一切都会好的.这意味着该CloseHandle调用消除了文件映射内核对象的最后引用计数并将其销毁.这反过来意味着MapViewOfFile实际上不会增加​​对象的引用计数.

我想确定发生了什么,以及MapViewOfFile关于文件映射内核对象的引用计数的确切语义是什么.

c++ windows winapi reference-counting memory-mapped-files

6
推荐指数
1
解决办法
579
查看次数