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

时间:2016-06-18 20:16:18

标签: c++ c++14 restrict strict-aliasing

The Problem

The restrict keyword in C is missing in C++, so out of interest I was looking for a way to emulate the same feature in C++.

Specifically, I would like the following to be equivalent:

// C
void func(S *restrict a, S *restrict b)

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

where noalias<T, n>

  • behaves just like T* when accessed with -> and *
  • can be constructed from an T* (so that the function can be called as func(t1, t2), where t1 and t2 are both of type T*)
  • the index n specifies the "aliasing class" of the variable, so that variables of type noalias<T, n> and noalias<T, m> may be assumed never to alias for n != m.

An Attempt

Here is my deeply flawed solution:

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
};

When accessed with ->, it casts the internally-stored T* to a noalias<T, n>::T2* and returns that instead. Since this is a different type for each n, the strict aliasing rule ensures that they will never alias. Also, since T2 derives from T, the returned pointer behaves just like a T*. Great!

Even better, the code compiles and the assembly output confirms that it has the desired effect.

The problem is the static_cast. If t were really pointing to an object of type T2 then this would be fine. But t points to a T so this is UB. In practice, since T2 is a subclass which adds nothing extra to T it will probably have the same data layout, and so member accesses on the T2* will look for members at the same offsets as they occur in T and everything will be fine.

But having an n-dependent class is necessary for strict aliasing, and that this class derives from T is also necessary so that the pointer can be treated like a T*. So UB seems unavoidable.

Questions

  • Can this be done in c++14 without invoking UB - possibly using a completely different idea?

  • If not, then I have heard about a "dot operator" in c++1z; would it be possible with this?

  • If the above, will something similar to noalias be appearing in the standard library?

5 个答案:

答案 0 :(得分:1)

您可以使用__restrict__ GCC扩展名进行取消/别名。

来自文档

除了允许受限制的指针外,您还可以指定受限制的引用,这些引用表明引用在本地上下文中没有别名。

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

fn的主体中,rptr指向一个非混淆的整数,而rref指的是一个(不同的)未混淆的整数。 您还可以使用__restrict__作为成员函数限定符来指定成员函数的this指针是否为unaliased。

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

T::fn的正文中,这将具有有效的定义T *__restrict__ const this。请注意,__restrict__成员函数限定符的解释与constvolatile限定符的解释不同,因为它应用于指针而不是对象。这与实现受限指针的其他编译器一致。

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

答案 1 :(得分:0)

也许我不明白你的问题,但是从STANDARD C ++中删除了c restrict关键字,但几乎每个编译器都有他们的&#34; C限制&#34;当量:

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

和海湾合作委员会有__ restrict__:https://gcc.gnu.org/onlinedocs/gcc/Restricted-Pointers.html

如果您想要一个通用的定义,可以使用#define&#39; s

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

我不测试它,让我知道这是行不通的

答案 2 :(得分:0)

如果我们只讨论纯C ++标准解决方案,那么运行时检查是唯一的方法。我实际上甚至不确定这是否可能,因为C&#39的限制左值限定符的定义是强大的,即对象只能通过限制指针访问。

答案 3 :(得分:-1)

向C ++添加类似限制语义的正确方法是让标准定义受限引用和受限制指针的模板,使得像普通引用和指针一样工作的虚拟版本可以用C ++编码。虽然有可能生成在所有已定义的情况下都需要的模板,并且在所有不应该定义的情况下调用UB,但如果没有适得其反,那么这样做将是无用的,除非编译器被编程为利用有问题的UB来促进这样的优化。编程编译器以在代码使用为此目的而存在的标准定义类型的情况下利用此类优化比尝试识别可被利用的用户类型中的模式更容易和更有效,并且也不太可能有不良副作用。

答案 4 :(得分:-1)

我认为即使注意到的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无法模拟,并且必须由编译器直接支持。