C的限制关键字可以在C++中使用严格的混叠来模拟吗?

Can C's restrict keyword be emulated using strict aliasing in C++?

本文关键字:模拟 关键字 C++      更新时间:2023-10-16

问题

C 中缺少C中的restrict关键字,因此出于兴趣,我正在寻找一种在C 中模仿相同功能的方法。

具体来说,我希望以下等效:

// C
void func(S *restrict a, S *restrict b)
// C++
void func(noalias<S, 1> a, noalias<S, 2> b)

其中 noalias<T, n>

  • ->*访问T*的行为就像CC_3
  • 可以从T*构造(以便将函数称为func(t1, t2),其中t1t2均为T*类型)
  • 索引n指定了变量的"混叠类",因此可以假定noalias<T, n>noalias<T, m>类型的变量永远不会对n!= m。

尝试

这是我深处有缺陷的解决方案:

template <typename T, int n>
class noalias
{
    struct T2 : T {};
    T *t;
public:
    noalias(T *t_) : t(t_) {}
    T2 *operator->() const {return static_cast<T2*>(t);} // <-- UB
};

使用->访问时,它将内部存储的T*投入到noalias<T, n>::T2*并返回。由于这是每种n的不同类型,因此严格的别名规则可确保它们永远不会别名。另外,由于T2源自T,因此返回的指针的行为就像T*。太好了!

更好的是,代码编译和组装输出确认其具有所需的效果。

问题是static_cast。如果t确实指向T2类型的对象,则可以。但是t指向T,所以这是UB。实际上,由于T2是一个子类,它对T没有任何额外的添加,因此可能具有相同的数据布局,因此T2*上的成员访问将在T中的相同偏移中寻找成员,并且一切都很好。<<<<<<<<<<<<<<<<<<<<<<<</p>

但是,具有n依赖性类是严格的混叠所必需的,并且此类源自T也是必要的,以便可以将指针视为T*。因此,UB似乎不可避免。

问题

  • 可以在C 14中完成此操作而不调用UB-可能使用完全不同的想法?

  • 如果没有,那么我在C 1z中听说过"点运算符";这样可以吗?

  • 如果以上,是否会出现在标准库中?

您可以使用 __restrict__ gcc扩展名进行联合/别名。

来自文档

除了允许限制指针外,您还可以指定有限的参考文献,该引用表明该参考文献在本地上下文中不明化。

void fn (int *__restrict__ rptr, int &__restrict__ rref)
{
/* ... */
}

fn的主体中,rptr指向一个非偏见的整数,而RREF指的是(不同的)非偏置整数。您还可以通过使用__restrict__作为成员函数预选程序来指定成员函数的该指针是否不明化。

void T::fn () __restrict__
{
/* ... */
}

T::fn的主体内,这将具有有效的定义T *__restrict__ const this。请注意,__restrict__成员函数预选赛的解释与constvolatile预选赛的解释不同,因为它应用于指针而不是对象。这与实施限制指针的其他编译器一致。

与所有最外部参数限定符一样,__restrict__在函数定义匹配中被忽略。这意味着您只需要在函数定义中指定 __restrict__,而不是在函数原型中指定。

也许我不明白您的问题,但是C限制关键字已从标准C 中删除,但几乎每个编译器都具有其" C限制"等效物:

Microsoft vs具有__declSpec(限制):https://msdn.microsoft.com/en-us/library/8bcxafdh.aspx

和GCC具有__限制__:https://gcc.gnu.org/onlinedocs/gcc/restrictal-pointers.html

如果您想要一个通用的定义,则可以使用#define的

#if defined(_MSC_VER)
#define RESTRICT __declspec(restrict)
#else
#define RESTRICT __restrict__
#endif

我不测试它,让我知道是不起作用

如果我们只是在谈论纯C 标准解决方案,运行时检查是唯一的办法。实际上,考虑到C的定义限制LVALUE预选赛的强度,我什至不确定是否可以,这是该对象只能通过限制指针访问。

添加类似限制语义的语义到c 的正确方法是使标准定义模板限制参考和受限制指针,以使得像普通参考和指针一样工作的虚拟版本可以在C 中进行编码。虽然可以生成在所有定义情况下按要求行事的模板,并在所有情况下都应调用UB,但除非编程编程以利用有关的UB来利用所涉及的UB,否则这样做将是无用的。这样的优化。在代码使用为此目的的标准定义类型的情况下,对编译器进行编程来利用此类优化,而不是试图在用户类型中识别它可以利用的模式更容易,更有效要具有不希望的副作用。

我认为即使著名的UB不存在,您的解决方案也无法完全实现预期的目标。毕竟,所有实际数据访问都会在内置类型级别上发生。如果decltype(a->i)int,并且您的功能操纵int*指针,则在某些情况下,编译器仍应假设这些指针可以别名a->i

示例:

int func(noalias<S, 1> a) {
    int s = 0;
    int* p = getPtr();
    for ( int i = 0; i < 10; ++i ) {
        ++*p;
        s += a->i;
    }
    return s;
}

使用noalias的用法几乎不会启用上述功能的优化:

int func(noalias<S, 1> a) {
    *getPtr() += 10;
    return 10 * a->i;
}

我的感觉是restrict不能模仿,必须直接由编译器支持。