gpt4 book ai didi

c++11 - Intel C++ 无法将 `T **` 转换为 `T const * const *` ,GCC 可以

转载 作者:行者123 更新时间:2023-12-04 12:45:52 25 4
gpt4 key购买 nike

问题

现有代码的扩展

我有一个数字库,它的设计考虑了一种“风格”。
现在我想概括一下。基本的数据结构是一个“微调”
本身就是一个多维矩阵。有很多功能需要
这些旋量的阵列。广义函数需要采用一个这样的旋量
每种口味的数组。

假设有一个函数,它至少执行以下操作:

void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}

我现在的概括是这样的:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}

在真正的代码中,在所有 num_flav上都有一个循环。 ,但这并不是真的
此处演示所需。

据我了解,必须将其读为 const Spinor *(in[num_flav]) ,
所以 in是一个指向可能是 num_flav 的数组的指针元素(或其他
数量因为 foo[]只是 *foo在函数参数中)类型
指向const-spinor的指针。

问题是它在使用 Spinor *non_const[2] 时无法编译
(没有 const ),见 my earlierquestion .从那里的答案我
已经了解到这不能编译,因为在函数内 copy_spinor ,指针 non_const[0]可以指向一些 const Spinor * 的数组.然后 non_const将指向 const数据。
因此这是行不通的。

我的结论是添加另一个 const将使它正确:
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}

当我现在通过我的 non_const作为第二个参数,该函数不能
in[0]任何东西,因为那个指针现在是不可变的。这有
GCC 6.3 对我很有帮助。现在使用 Intel C++ 17 投入生产,它
不再起作用。

最小的工作示例如下:
#include <cstdint>

typedef float Spinor[3][4][2][8];

template <uint8_t num_flav>
class Solver {
public:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}

void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}
};

int main(int argc, char **argv) {
Spinor *s1 = new Spinor[10];
Spinor *s2 = new Spinor[10];

Spinor *s1_a[1] = {s1};
Spinor *s2_a[1] = {s2};

Solver<1> s;

s.copy_spinor(s2_a, s1_a);
}

在 GCC 上,它显然解析为第二个 copy_spinor重载。这
变量 s1_a它承担了之前 non_const 的角色, 允许为
一个论点。

英特尔 C++ 17 的问题

然而,英特尔 C++ 17 不接受:
$ icpc -Wall -pedantic const-spinor-const.cpp  --std=c++11
const-spinor-const.cpp(23): error: no instance of overloaded function "Solver<num_flav>::copy_spinor [with num_flav=(uint8_t={unsigned char})'\001']" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
object type is: Solver<(uint8_t)'\001'>
s.copy_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(11): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
^
const-spinor-const.cpp(10): note: this candidate was rejected because arguments do not match
void copy_spinor(Spinor *out, const Spinor *in) {}
^

错误消息不是特别有用,因为它没有说明什么
不允许转换。似乎只是 const是问题所在。

也许我想念英特尔 C++ 的某些东西?是否缺少功能或
我是否使用了非官方的 GCC 扩展?这是英特尔 C++ 中的错误还是
海湾合作委员会?

更新:该示例还使用当前的 Clang 进行编译。

非模板类

当类 Solver 时,同样的问题也仍然存在。不是模板类。
T a[2]T a[2] 相同和 T *a在函数参数中,我可以
也只写这样的函数,不需要 uint8_t num_flav :
void copy_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}

错误是一样的。

免费功能

非成员非友非模板函数也会发生同样的问题:
void free_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}

void free_spinor(Spinor *out[], const Spinor *const in[]) {
std::cout << "Fwd: ";
free_spinor(out[0], in[0]);
}

错误消息是一样的:
$ icpc -Wall -pedantic const-spinor-const.cpp  --std=c++11
const-spinor-const.cpp(97): error: no instance of overloaded function "free_spinor" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
free_spinor(s2_a, s1_a);
^
const-spinor-const.cpp(30): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out[], const Spinor *const in[]) {
^
const-spinor-const.cpp(26): note: this candidate was rejected because arguments do not match
void free_spinor(Spinor *out, const Spinor *in) {
^

解决方案尝试

为了在生产中运行代码,我看到以下选项。没有
他们特别有吸引力。

什么是前进的好方法?我可以改变我的新功能
想要,但我想尽可能避免接触调用者代码。

常量包装器

当我更改 s1_a 的定义时在 main功能有两个 const ,它编译:
const Spinor *const s1_a[1] = {s1};

然后函数 copy_spinor使用正确的参数类型调用。

通用代码的每个用户都必须编写这些 const每个函数调用的包装器。那会变得非常困惑。

删除常量正确性。

我可以删除最左边的 const从函数参数参数。那
在两个编译器上都可以干净地编译。但是,我确实想证明我是
不改变该数组中的任何内容,因此它的值应该是 const .

部分解决方案是使用一些预处理器常量来删除 const仅适用于英特尔编译器。
#ifdef __INTEL_COMPILER
#define ICPC_CONST
#else
#define ICPC_CONST const
#endif

也许用户已经将一些 spinor 定义为 const。然后我被卡住了,需要
有 const 到位:
const Spinor *s3_a[1] = {s3};

s.copy_spinor(s2_a, s3_a);

添加 const 应该更容易而不是删除它,所以这个解决方案是
相当缺乏。此外,上游作者可能会拒绝我的更改,因为
他的代码的变化。

为每个函数添加一个非常量重载

为每个函数添加一个重载是可能的。我有两种变体
通用功能,当我使用
英特尔编译器:
void copy_spinor(Spinor *out, const Spinor *in) {
std::cout << out << " " << in << "\n";
}

void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}

#ifdef __INTEL_COMPILER
void copy_spinor(Spinor *out[num_flav], Spinor *const in[num_flav]) {
std::cout << "Fwd2: ";
copy_spinor(out[0], in[0]);
}
#endif

效果很好,只是有一些重复的代码。自从我添加
函数只是重用已有的函数,代码不多
复制。仍然违反了 DRY 原则。

另一个缺点是重载次数为 2^N其中 N 是 const * 的参数数量.有函数占用
像这样的三个参数,因此我需要八份。

让模板推导出常数

抽象 const SpinorSpinor使用模板是可能的。一世
可以将函数编写为模板,使得 S可以是任一数据类型。
使用 static_assert将给出一个稍微多一点的错误信息。
template <typename S>
void copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
static_assert(std::is_same<Spinor, S>::value ||
std::is_same<const Spinor, S>::value,
"Template parameter must be `const Spinor` or `Spinor`.");

std::cout << "Fwd: ";
copy_spinor(out[0], in[0]);
}

理想情况下,我想指定 S只能是 Spinorconst
Spinor
.也许这在 C++14 及更高版本中是可能的,我必须坚持
C++11。

这个解决方案看起来很干净,我可以添加一个模板参数和一个断言
对于函数中的每个参数。这将扩展得很好,并且没有
代码重复。唯一的缺点可能是更长的编译时间(已经
相当长,但不是很重要)和不太有用的错误消息
(希望被 static_assert 覆盖)。

使用 int ** 调用时的错误信息以下是 GCC 的内容:
const-spinor-const.cpp: In instantiation of 'void Solver<num_flav>::t_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int; unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]':
const-spinor-const.cpp:86:36: required from here
const-spinor-const.cpp:40:9: error: static assertion failed: Template parameter must be `const Spinor` or `Spinor`.
static_assert(std::is_same<Spinor, S>::value ||
^~~~~~~~~~~~~
const-spinor-const.cpp:45:20: error: no matching function for call to 'Solver<1u>::copy_spinor(float (*&)[3][4][2][8], int* const&)'
copy_spinor(out[0], in[0]);
~~~~~~~~~~~^~~~~~~~~~~~~~~
const-spinor-const.cpp:29:10: note: candidate: void Solver<num_flav>::copy_spinor(float (*)[3][4][2][8], const float (*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out, const Spinor *in) {
^~~~~~~~~~~
const-spinor-const.cpp:29:10: note: no known conversion for argument 2 from 'int* const' to 'const float (*)[3][4][2][8]'
const-spinor-const.cpp:33:10: note: candidate: void Solver<num_flav>::copy_spinor(float (**)[3][4][2][8], const float (* const*)[3][4][2][8]) [with unsigned char num_flav = 1u; Spinor = float [3][4][2][8]]
void copy_spinor(Spinor *out[num_flav], const Spinor *const in[num_flav]) {
^~~~~~~~~~~
const-spinor-const.cpp:33:10: note: no known conversion for argument 1 from 'float (*)[3][4][2][8]' to 'float (**)[3][4][2][8]'

在评论中指出使用 enable_if .有了这个,我的功能
如下所示:
template <typename S>
typename std::enable_if<std::is_same<const Spinor, const S>::value,
void>::type
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
std::cout << "Fwd: " << typeid(S).name() << " " << typeName<S>() << " ";
copy_spinor(out[0], in[0]);
}

这更短,也许更简洁。错误信息不包含
不过,我不再是手写的消息了。至少不会出现错误
函数内 copy_spinor但是在调用站点,所以用户知道
哪里出错了。那也许更好。和 enable_if有些
至少向更有经验的模板用户解释自己。
const-spinor-const.cpp: In function 'int main(int, char**)':
const-spinor-const.cpp:86:37: error: no matching function for call to 'Solver<1u>::t2_copy_spinor(float (* [1])[3][4][2][8], int* [2])'
s.t2_copy_spinor(s2_a, int_array);
^
const-spinor-const.cpp:51:5: note: candidate: template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = S; unsigned char num_flav = 1u]
t2_copy_spinor(Spinor *out[num_flav], S *const in[num_flav]) {
^~~~~~~~~~~~~~
const-spinor-const.cpp:51:5: note: template argument deduction/substitution failed:
const-spinor-const.cpp: In substitution of 'template<class S> typename std::enable_if<std::is_same<const float [3][4][2][8], const S>::value, void>::type Solver<num_flav>::t2_copy_spinor(float (**)[3][4][2][8], S* const*) [with S = int]':
const-spinor-const.cpp:86:37: required from here
const-spinor-const.cpp:51:5: error: no type named 'type' in 'struct std::enable_if<false, void>'
enable_if解决方案看起来比 static_assert 更好变体。

最佳答案

GCC 和 clang 就在这里,Intel C++ 是错误的。

该标准的相关部分是 资质转换 [conv.qual] (节号可以是 4.4 或 4.5)。 C++11 和 C++1z(变成 C++17)之间的措辞发生了变化……但是你的代码添加了 const在从最浅的多个级别开始,在所有版本 (C++03, 11, 14, 1z) 中都是允许的。

一个值得注意的变化是多级 const 规则现在适用于指针数组,以前它只适用于多个指针。但我们实际上是在处理一个多指针,因为数组语法,当在参数声明中找到时,具有指针语义。

尽管如此,还是值得一试

void copy_spinor(Spinor **out, const Spinor *const *in)

以防编译器在应用规则之前感到困惑/未能将数组类型调整为函数参数列表中的指针类型。

关于c++11 - Intel C++ 无法将 `T **` 转换为 `T const * const *` ,GCC 可以,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43066405/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com