现在我们知道,越界 - 指针算术具有未定义的行为,如本SO问题所述.
我的问题是:我们可以通过转换为std :: uintptr_t进行算术运算然后转换回指针来解决这种限制吗?是保证工作吗?
例如:
char a[5];
auto u = reinterpret_cast<std::uintptr_t>(a) - 1;
auto p = reinterpret_cast<char*>(u + 1); // OK?
Run Code Online (Sandbox Code Playgroud)
真实世界的用法是优化偏移内存访问 - 而不是p[n + offset]
我想做的offset_p[n]
.
编辑使问题更明确:
给定一个p
char数组的基指针,如果p + n
是一个有效的指针,将reinterpret_cast<char*>(reinterpret_cast<std::uintptr_t>(p) + n)
保证产生相同的有效指针?
例:
int main(int argc, char**)
{
constexpr int a = argc * 0;
(void)a;
constexpr int b = argc - argc;
(void)b;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
argc
不是常量表达式,但编译器仍能够在两种情况下计算结果a
和b
编译时间(即0
).
标准是否允许编译器与g ++一样聪明constexpr
?
扣除f1
和f2
形成不良?
template<class... T, class U>
void f1(T..., U){}
template<class... T>
void f2(T..., int){}
int main()
{
f1(1);
f2(1);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
g ++接受两者,clang只接受f2
,而msvc拒绝这两者.
相关标准措辞:
当函数参数包出现在非推导的上下文([temp.deduct.type])中时,永远不会推导出该参数包的类型.
未推断的上下文是:
- 函数参数包,不会出现在参数声明列表的末尾.
所以似乎MSVC拒绝两者都是正确的?
这是否意味着即使您明确指定模板args,模板的任何实例化都将是格式错误的?
f1<int>(1, 2); // ill-formed?
f2<int>(1, 2); // ill-formed?
Run Code Online (Sandbox Code Playgroud)
如果是这样的话,为什么要在第一时间允许这样的声明呢?
我刚开始玩Boost.Compute,看看它能为我们带来多少速度,我写了一个简单的程序:
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/foreach.hpp>
#include <boost/compute/core.hpp>
#include <boost/compute/platform.hpp>
#include <boost/compute/algorithm.hpp>
#include <boost/compute/container/vector.hpp>
#include <boost/compute/functional/math.hpp>
#include <boost/compute/types/builtin.hpp>
#include <boost/compute/function.hpp>
#include <boost/chrono/include.hpp>
namespace compute = boost::compute;
int main()
{
// generate random data on the host
std::vector<float> host_vector(16000);
std::generate(host_vector.begin(), host_vector.end(), rand);
BOOST_FOREACH (auto const& platform, compute::system::platforms())
{
std::cout << "====================" << platform.name() << "====================\n";
BOOST_FOREACH (auto const& device, platform.devices())
{
std::cout << "device: " << device.name() << std::endl;
compute::context context(device);
compute::command_queue queue(context, device);
compute::vector<float> device_vector(host_vector.size(), context);
// …
Run Code Online (Sandbox Code Playgroud) 给定一个非可变函数模板:
template<class T>
void f(void(t)(T));
Run Code Online (Sandbox Code Playgroud)
还有一些简单的功能:
void f1(int);
void f2(char);
Run Code Online (Sandbox Code Playgroud)
这有效:
f(f1);
Run Code Online (Sandbox Code Playgroud)
类型t
变成了void (*)(int)
.
但是,可变对应物:
template<class... T>
void f(void(...t)(T));
// call
f(f1, f2);
Run Code Online (Sandbox Code Playgroud)
不起作用.编译器(gcc&clang)抱怨不匹配的类型void(T)
和void (*)(int)
.见DEMO.
请注意,如果*
明确添加,它可以正常工作:
template<class... T>
void f(void(*...t)(T));
Run Code Online (Sandbox Code Playgroud)
那么,为什么非可变参数可以衰减函数类型而可变参数不能?
c++ language-lawyer function-templates variadic-templates c++11
在看到SO答案后,我可能有点迟到知道这个标准陈述:
[C++ 11:7.5/1]
具有不同语言链接的两种函数类型是不同的类型,即使它们在其他方面是相同的.
这意味着,给定:
void f1();
extern "C" void f2();
Run Code Online (Sandbox Code Playgroud)
decltype(f1)
是不一样的 decltype(f2)
直到现在我才意识到的一个原因是主要的编译器(例如g ++,clang,vc ++ ......)不遵守这个规则.见现场演示.
但我相信大多数人(包括我)会对当前不符合标准的行为感到高兴.如果编译器遵循标准,许多桥接C和C++的代码将被破坏.
考虑这个例子:
库提供C API:
#ifdef __cplusplus
extern "C" {
#endif
void registerCallbackInC(void(*callback)(void*));
#ifdef __cplusplus
}
#endif
Run Code Online (Sandbox Code Playgroud)
要在C++中使用库:
void f1(void*)
{
...
}
extern "C" void f2(void*)
{
...
}
registerCallbackInC(f1); // invalid, f1 has C++ linkage
registerCallbackInC(f2); // OK
Run Code Online (Sandbox Code Playgroud)
要使用registerCallbackInC
,callback
必须有C-linkage,但是,我们不能使用extern "C"
模板:
extern "C"
{
template<class T>
void f(void*); // invalid …
Run Code Online (Sandbox Code Playgroud) C++ 17引入了std::aligned_alloc
对齐感知new
,它可以进行过度对齐的分配,但是呢std::allocator
?它是否处理过度对齐的类型?