原子函数指针调用GCC中的编译,但不在Clang和MSVC中
当从原子函数指针调用函数时,例如:
#include <atomic>
#include <type_traits>
int func0(){ return 0; }
using func_type = std::add_pointer<int()>::type;
std::atomic<func_type> f = { func0 };
int main(){
f();
}
GCC根本不抱怨,而Clang和MSVC在CALL f()
中有问题:
- [clang]:错误:error:呼叫到类型的对象' std :: atomic&lt; func_type&gt;' (又名'atomic&lt; int(*)()()&gt;')是模棱两可的
- [msvc]:类型为“ std :: atomic&lt; func_type&gt; func_type&gt; func_type&gt;”的对象不止一种。可以为参数列表调用
clang clang另外指定可能的呼叫候选者为:
操作员__pointer_type()const noexcept
operator __pointer_type()const const volatile noexcept
似乎在波动性的差异似乎就像Clang和MSVC令人困惑,但没有GCC。
当调用从f()
更改为f.load()()
时,代码在所有上述编译器中都可以使用。这是更令人困惑的,因为两个 load> load> load()
> 和 operator t()
据说具有const
和const volatile
过载 - 如果隐式转换不起作用,我希望load()
不工作也是如此。在隐式转换(与成员调用)中,规则有所不同吗?
那么,GCC接受该代码是错误的吗? Clang和MSVC错误地出错了吗?还有其他错误或正确的组合吗?
这主要是一个理论上的问题,但是如果有更好的方法可以使用原子功能指针,我想知道。
When calling function from an atomic function pointer, like:
#include <atomic>
#include <type_traits>
int func0(){ return 0; }
using func_type = std::add_pointer<int()>::type;
std::atomic<func_type> f = { func0 };
int main(){
f();
}
gcc doesn't complain at all, while clang and msvc have problem with call f()
:
- [clang]: error: call to object of type 'std::atomic<func_type>' (aka 'atomic<int (*)()>') is ambiguous
- [msvc]: there is more than one way an object of type "std::atomic<func_type>" can be called for the argument list
Clang additionally specifies possible call candidates to be:
operator __pointer_type() const noexcept
operator __pointer_type() const volatile noexcept
It seems like this difference in volatility is confusing for clang and msvc, but not gcc.
When call is changed from f()
to f.load()()
, the code works in all abovementioned compilers. Which is all the more confusing, since both load()
and operator T()
are said to have const
and const volatile
overloads - if implicit conversion doesn't work, I'd expect load()
not to work as well. Are the rules somehow different within implicit conversions (versus member calls)?
So, is gcc wrong to accept that code? Are clang and msvc wrong to error out? Any other combination of being wrong or right?
This is mostly a theoretical question, but if there is some better way to have an atomic function pointer, I'd like to know.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Clang和MSVC是正确的。
对于每个转换函数到类的功能指针,将所谓的替代呼叫函数添加到过载分辨率中,如果选择将首先通过此操作员过载将对象转换为功能指针,然后通过功能指针调用功能。这在 [over.call.object]/2 。
但是,替代呼叫功能不会以任何方式转换转换操作员的CV Qualifier。因此,由于
std :: Atomic
具有volatile
的转换操作员,而不是一个不可分割的替代呼叫函数。这些也是唯一的候选人,因为std :: atomic
没有任何实际operator()
,因此超载分辨率必须始终模棱两可。标准中甚至有一个脚注,即可能发生这种情况,请参见 [over.call.Object]/footnote.120 。
直接调用
.load()
volatile
-qualifier将是超载分辨率的打pluck剂,因此不会出现此问题。使用
(*f)()
在(内置)运算符上的Overload分辨率*
带有功能指针类型作为参数。通过两个转换函数有两个隐式转换序列。该标准并不十分清楚,但我认为这不是导致含糊的转换序列(选择时也意味着分辨率模棱两可)。取而代之的是,我认为将通过转换函数初始化的规则应用于仅选择其中一种转换,这将使之明确地使其成为volatile
Qualified一个。Clang and MSVC are correct.
For each conversion function to a function pointer of the class, a so-called surrogate call function is added to overload resolution, which if chosen would first convert the object via this operator overload to a function pointer and then call the function via the function pointer. This is explained in [over.call.object]/2.
However, the surrogate call function does not translate the cv-qualifiers of the conversion operator in any way. So, since
std::atomic
has a conversion operator which isvolatile
and one which is not, there will be two indistinguishable surrogate call functions. These are also the only candidates sincestd::atomic
doesn't have any actualoperator()
and so overload resolution must always be ambiguous.There is even a footnote in the standard mentioning that this can happen, see [over.call.object]/footnote.120.
With a direct call to
.load()
thevolatile
-qualifier will be a tie-breaker in overload resolution, so this issue doesn't appear.With
(*f)()
overload resolution on the (built-in)operator*
with the function pointer type as parameter is performed. There are two implicit conversion sequences via the two conversion functions. The standard isn't very clear on it, but I think the intention is that this doesn't result in an ambiguous conversion sequence (which would also imply ambiguous overload resolution when it is chosen). Instead I think it is intended that the rules for initialization by conversion function are applied to select only one of the conversions, which would make it unambiguously thevolatile
-qualified one.