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.
(*f)();
but I am not sure what the difference is - I guess it's the same asf.load()()
(as above). – Unbraid(*f)()
is more likef()
, becauseoperator T()
is used implicitly for both (whereas there's no conversion inf.load()()
). – Chemise