This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
工具/软件:TI C/C++编译器
在我的应用中、我必须在函数指针中存储 fmod、pow、atan2、fmax 和 fmin 的双过载地址。 这在 CGT 8.3.x 中很难实现(我测试了8.3.0和8.3.6)。
尝试编译:
//最小示例 #include int main() { double works = fmod (1.0、3.0); auto broken = static_cast (&FMod); 返回工作!=断开(1.0、3.0); }
使用命令:
$cgt_path/bin/cl6x -i$cgt_path/include overload_resolution.cpp
产生以下输出:
"μ C/ti-CGT-C6000_8.3.6/include/libcxx/math.h"、第893行:~:静态断言失败、出现"" 在"std:__2:__lazy_enable_if<的实例化期间检测到 ,std::__2:__promote <_A1, _A2, void>:在 编译"overy_resolution .cpp"时检测到"overy_resolution.cppp"的第6行中键入 fmod (_A1、_A2)[带_a1=double、_a2=double]"。 >>编译失败
GCC、clang 和 MSVC 均可编译此最小示例、而不会出现任何问题。
查看断言、编译器决定在$cgt_path/include/libcxx/math.h 中定义的模板与在$cgt_path/include/math.h 中声明的 C 函数 double fmod (double、double)的匹配
进一步的分析表明、编译器得出这一结论是因为模板具有正常的 C++链接、而 C 函数具有'extern "C"链接:
//分析 extern "C" double foo (double、double) { return 0.0; } 模板 t3 foo (t1、t2) { static_assert (sizeof (t1)=0、"选择的错误过载"); return 1.0; } int main() { auto fp = static_cast (&foo); 返回 static_cast (fp (1.0、2.0)); }
产生的
"OVERLOAD_RESolution.cpp"、第10行:错误:静态断言失败、并显示"Selected Wrong OVERLOAD (选择了错误的过载)" 在 "overy_resolution.cpp"编译中检测到的第16行"T3 foo (T1、T2)[ t1=double、t2=double、t3=double]"实例化期间检测到错误。 >>编译失败
而
//分析 /*extern "C"*/ double foo (double、double) { 返回0.0; } 模板 t3 foo (t1、t2) { static_assert (sizeof (t1)=0、"选择的错误过载"); return 1.0; } int main() { auto fp = static_cast (&foo); 返回 static_cast (fp (1.0、2.0)); }
编译而不会出现问题。
我可以想象、模板与'extern "C"函数的匹配是正确的、但我希望有一个具有 C++链接的包装程序(类似于 float fmod (float、float))。
在找出根本原因后、我获得了一个可以编译的权变措施:
//变通办法 #include extern "C" { auto cppfmod = static_cast (&FMOD); } int main() { double works = FMod (1.0, 3.0); auto broken = cpfmod; return works!= broken (1.0, 3.0); }
感谢您通知我们此问题、并做了大量工作来描述和分析此问题。
我提交了 EXT_EP-9742条目 以进行调查。 欢迎您使用我签名中的以下链接进行操作。
谢谢、此致、
乔治
尊敬的 Dennis:
该标准目前规定、语言链接是该类型的一部分、因此 C++和 C 链接函数类型构成了不同的候选函数集以实现解析。 这在 C++ 14中没有变化。 7.5.1中的相关直接引述是:"两种具有不同语言链接的函数类型是不同类型的、即使它们在其他方面是相同的。"
虽然大多数编译器放宽了这一要求、但我们过去没有、这样做可能会导致向后兼容性问题、因此我们必须仔细考虑是否要更改它。
您可以在 extern "C"中为所需的函数类型换行 typedef、以使代码能够按预期进行编译、 但我认为这不是一个“好”的解决方案,因为 FMod 超载的目的是为不是双精度(*)(双精度、双精度)的呼叫提供备用候选。
正如我在原始帖子中所说:
[引用用户="Dennis Flori"]
我可以想象、模板与'extern "C"函数的匹配是正确的、但我希望有一个具有 C++链接的包装程序(类似于 float fmod (float、float))。
[/报价]
我最初的想法确实是要对'extern "C"过载提出一个问题、而不是被认为是更好的过载、但我注意到您也提到的标准中的确切一点、因此我决定对我的问题重新措辞。 :-) 我要做的(以及我认为应该能够做的)是获取 FMod 的双重过载地址。 这以前是可能的(至少达到 CGT 8.0.1)。
我认为所发生的情况如下:CGT 8.3 添加了对 C++14的支持、(自 C++11起)包含模板过载
提升的 FMod ( Arithmic1 x,Arithmic2 y );
在编译器中、此模板现在比更匹配
extern "C" double fmod (double、double)
过载(请注意:在标准中、我找不到任何表明双 fmod 过载具有'extern "C"链接的内容、但在您的标题中、它具有)。
但是、此模板过载不适用于双模板(双模板)。 double)、因此它具有一个 static_assert、该断言显式检查模板是否未实例化以实现标准中的其他过载之一:
float 闪存(浮点 x,浮点 y); 双精度 FMod ( double x, double y); long double fmod ( long double x, long double y);
然后、该 static_dassert 会中断我的最小示例的构建。
就我所知、CGT8.3.x 的 math.h 基于 LLVM 的 math.h、但 LLVM 的编译器在语言链接和过载分辨率方面具有不同的行为、因此在尝试构建我的最小示例时、LLVM 没有相同的问题。
如果您同意可以使用 double fmod (double、double)的地址、我认为您有2个选项:
我了解到、由于向后兼容性(虽然会增加与主要编译器的兼容性)、选项1不是首选项、因此将保留选项2。 我想删除 static_dassert 可能同样简单、但最好只添加一个显式双 fmod (double、double)包装器、正如我在原始帖子中建议的那样。
在进行一些初步研究时、这里显示的一条信息是、外部"C" Fmod 显然完全处于过载分辨率设置中、或者如果没有其他定义、则被放置在该设置中。
extern "C" double fmod (double、double); int main () { //解析、但我怀疑它不应该 自动断开= static_cast (&FMod); 返回断开(1.0、3.0); }
事实上、我们的编译器允许在需要诊断时从 extern "C"隐式转换为 extern "C++"。 这就是为什么我认为不应该使用上述代码的原因、以及为什么它会在 Fmod 存在时立即拾取外部"C++"模板定义。
实际上、如果我们通过 enable_if 消除模板过载(定义取自 标头):
模板 Typename std::enable_if < std::is 算术 <_A1>:::值&& std::is 算术 <_A2>:::值&& //确保大多数参数都是"双"的 !(std::is 相同 <_A1, double>::value && std::ies_same <_A2, double>::值)、 标准::__promote <_A1, _A2> ::键入 fmod (_A1 _lcpp_x、_A2 _lcpp_y) { typedef 类型名 std:__promote <_A1, _A2>:键入__Result_type; static_assert (!(std:is_same <_A1, __result_type>:::值&& 标准::IS 相同 <_A2, __result_type>::value))、""); 返回::fmod ((__Result_type)__lcpp_x,(__Result_type)__lcpp_y); }
由于这种隐式转换、程序也会选择 extern "C"声明。 总之、我正在从手头的原始主题中挖掘出来。
我认为、采用 FMod 的地址是实施 定义的。
允许实现将 extern "C"和 extern "C++"视为不同的(对于符合标准的实现、必须这样做)。 根据 C++14标准的17.6.2.3第2段、还允许实现使用 extern "C"标记从 C 库导入的函数。
他们建议使用 extern "C++"、但这对于以下产品是不可行的:
为导入的库函数使用 extern "C++"意味着将有一个从 C 和 C++调用的每个函数的不同副本。
这并不意味着我们不会考虑本报告的更改、但我认为这不是一个错误、而是一个朝着更类似 LLVM/Gcc 的实现方向发展的请求。 我不认为我们反对这一点、但正如我之前所说的、由于向后兼容性、这是一项更大的任务。
我相信您已经尝试了以下操作、但它允许您获得所需的行为:
#include extern "C"{使用 FMod_t = double (*)(double、double);} int main() { double works = fmod (1.0、3.0); auto broken = static_cast (&FMod); 返回工作!=断开(1.0、3.0); }