Why did newer compilers start accepting this template function call? - c++

The following small C++ program involving a call to the template function std::atomic_fetch_add() fails to compile in godbolt for x86-64 clang versions less than 9.0 and gcc versions less than 9.1,
in both cases using the --std=c++11 option.
#include <iostream>
#include <atomic>
std::atomic<char> ch ('#');
int main ()
{
std::atomic_fetch_add (&ch, 5);
std::cout << ch << std::endl;
}
In those early compiler versions (it might be more related to the C++ libraries provided by the compilers, I'm not sure), the std::atomic_fetch_add (&ch, 5); call fails to match any template specialization for the atomic fetch function, since the type of ch is char, and the type of 5 is int:
: In function 'int main()':
:8:34: error: no matching function for call to 'atomic_fetch_add(std::atomic*, int)'
std::atomic_fetch_add (&ch, 5);
...
However, later versions of the compiler (& libraries?) successfully compile this usage.
What changed to make this start compiling?
If it is a standard library change, what technique is used to allow this?
Does the C++ standard (& what version?) require that this usage should work.
I'm pretty much a C++ beginner, but I gather that this is related to implicit conversions not being performed as part of figuring out the appropriate template specialization to use. (I may not be using precise language.)
I know that I can modify the call to work by rewriting it as either
std::atomic_fetch_add<char> (&ch, 5);
or
std::atomic_fetch_add (&ch, (char)5);
However, I'm interested in getting a version of the <atomic> library to support the usage without the explicit instantiation and without the cast. (The compiler is clang 15.0.0, --std=c++11. The library is a proprietary version of the Dinkum libraries.)
I need to understand how to support the calling of template functions like
std::atomic_fetch_char (&ch, 5) where the argument types do not exactly match those in the template declaration. I would, for example, like to understand how this is supported in the linux /usr/include/c++/11/ standard library.
The nearest I've come up with myself is to add non-template overloads of various instantiations of std::atomic_fetch_add() & similar functions, but I don't see that being done in the Linux/GNU C++ libraries and I suspect there is a cleaner way. I want to understand it.

Originally std::atomic_fetch_add required the second parameter to have the same type as the value_type of the atomic object as both parameters participated in type deduction.
This was removed with defect report P0558R1 so that deduction only happens on the atomic object and the second parameter is just converted to the differece_type of the atomic object.
You get the error because older versions didn't get patched with this defect report but newer versions did.

Related

Compile-time error in uninstanciated function template

My understanding of function templates has always been: if they contain invalid
C++ and you don't instanciate them, your project will compile fine.
However, the following code:
#include <cstdio>
#include <utility>
template <typename T>
void contains_compile_time_error(T&& t) {
int j = nullptr;
}
int main() {}
Compiles with:
x86-64 gcc 11.2 and flag -std=c++20;
x64 msvc v19.31 and flag /std:c++20;
and does not with x86-64 clang 14.0.0 (with flags -std=c++{11,14,17,20}):
error: cannot initialize a variable of type 'int' with an rvalue of type 'std::nullptr_t'
int j = nullptr;
^ ~~~~~~~
1 error generated.
Even more confusing to me, the following code:
#include <cstdio>
#include <utility>
namespace ns {
struct S {};
template <typename T>
void apply(T&& t) {
//print(std::forward<T>(t));
ns::print(std::forward<T>(t));
}
void print(S const&) { std::puts("S"); }
} // namespace ns
int main() {}
Does not compile with:
x86-64 gcc 11.2 and flag -std=c++20;
x64 msvc v19.31 and flag /std:c++20;
x86-64 clang 14.0.0 and flags -std=c++{11,14,17,20};
(all of them complaining that print is not a member of 'ns') but does compile with x64 msvc v19.31 /std:c++17.
If I call unqualified print, then the code compiles with all the above compilers.
So, my questions are:
is my understanding of function templates wrong?
why do the above compilers behave differently with the code snippets I posted?
Edit 0: as per Frank's comment, x64 msvc v19.31 /std:c++17 /permissive- fails to compile function template apply where I call the qualified ns::print.
My understanding of function templates has always been: if they contain invalid C++ and you don't instanciate them, your project will compile fine.
Nope, if the code in the template is invalid, then so is the program.
But what does "invalid C++" mean? You can have syntactically valid C++ that is still invalid semantically, and the semantics of C++ are highly contextual.
So there's multiple levels of "valid" that can be checked at different times during compilation, based on the available information. Critically, there are things that can theoretically be checked early, but are potentially unreasonably difficult. Because of this, compilers are allowed some leeway when it comes to validating the semantics of template definitions. However, whether the code is broken or is not ambiguous, it's a compiler's ability to detect broken code that is.
why do the above compilers behave differently with the code snippets I posted?
As far as int j = nullptr is concerned:
The program is technically ill-formed, but the diagnostics is optional. So The code is broken, but GCC is not breaking compliance by letting it through.
For example, if S only had private constructors, then print(std::forward<T>(t)) would be flaggable as being broken since there is no possibly T that would make it valid. However, requiring compilers to be smart enough to determine this in all cases would be kind of mean.
For print():
Lookups are a bit of a different matter, there are hard rules that are supposed to be followed.
There are three types of lookups involved here.
Qualified lookups, such as ns::print
Unqualified lookups not involving template parameters, which you'd get if you tried print(S{});
Unqualified lookups involving template parameters, such as print(std::forward<T>(t));
Lookups are deferred for the third category, but must still be performed the same as non-template functions for the other two.
Note that the code still needs to be syntactically valid, even in the case of deferred lookups, hence why typename needs to be added when doing a dependent lookup of a type.
And as for MSVC allowing ns::print: That compiler is not compliant by default on this (and other) aspect of the standard. You need to use the /permissive- compiler flag to enforce compliance.

Is operator void*() conversion still part of C++ library?

Consider this program:
#include <iostream>
int main()
{
delete std::cout;
}
AFAIK the conversion function operator void* () const has been removed from C++11. So, this program should fail in compilation on a C++11 compiler. Ya, its true that both g++ 4.8.1 & 4.9.2 gives diagnosis (in the form of warning that deleting void* is undefined & that's also the good thing). But shouldn't this program fail in compilation because removal of that conversion function due to which all stream object could be implicitly converted to void* in C++98 & C++03?. Is this bug? It seems bit surprising that they still not have implemented this change.
I've tried this program in g++ 4.9.2(that supports C++14) but it gives warning not compiler error. Ideone compiler gives me an error as expected. (See live demo here)
It has nothing to do with the compiler, its a library issue. libstdc++ has lots of incompatibilities with C++11, of which this is just one. They are making breaking changes in 5 and up though iirc.
In short, it's neither a bug nor a compiler issue.
This is a bug in the standard library (if you view it as an implementation of the C++11/14 standard library rather than C++98/03).
It's sort of a compiler issue as well though. Specifically, removing the conversion to void * depends on adding a conversion directly to bool--but that, in turn, depends on adding "contextual conversion" to the compiler.
gcc 4.8 did implement a form of contextual conversion, but not the form that was accepted into the standard. Although the specific changes to contextual conversion wouldn't directly impact this use of contextual conversion, it does point to the fact that the definition of contextual conversion was still being tweaked as these compilers were being written.
The sequence in which things (at least normally) happen is that first the specification is solidified. Then the compiler implements it. Then the standard library puts it to use.
In this case the specification was still changing fairly shortly before the compiler was released. Therefore, the standard library didn't (and practically speaking, couldn't) use it.
By the time of 4.9, the specification was solid, and the compiler implemented the final version of contextual conversion, but it hadn't been around long enough to be put to use in the standard library yet.

Visual Studio 2012 compiler alternative on Linux?

I switched to Linux a few days ago and I'm stuck on porting my project. For some reason, no matter if I'm using clang++ or gcc 4.8, the compiler complaints about using a atomic with a vector from the GLM-Library (something like that: atomic<glm::vec3>). I tried to set -std=c++11 and -std=gnu++11, which gcc doesn't accept and g++/clang++ don't care about. I know that it's not my code, because it worked on Windows with Visual Studio 2012.
So now my question is, if there is a good alternative to the VS2012 compiler, so that my code gets to work? I also thought to try gcc 4.9, but I really have no idea how to get that.
clang error message:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/atomic|166|error: exception specification of explicitly defaulted default constructor does not match the calculated one|
g++ error message:
/usr/include/c++/4.8/atomic|167|error: function ‘std::atomic<_Tp>::atomic() [with _Tp = glm::detail::tvec3<float>]’ defaulted on its first declaration with an exception-specification that differs from the implicit declaration ‘std::atomic<glm::detail::tvec3<float> >::atomic()’|
glm::vec3 is a typedef to some specialization of tvec<float, T>. tvec has a user-provided assignment operator, so it is not trivially copyable and therefore cannot be used with std::atomic. The code compiles with VS2012 because Microsoft's standard library implementation doesn't diagnose instantiations of std::atomic<T> for non-TriviallyCopyable T. As with any undefined behavior, the fact that it compiles does not imply that it works.
std::atomic<glm::vec3> // sizeof(...) == 12
This specialization does not exist. There are only specialization of bool, integral types and pointer types provided by the STL. Either you have to specialize it on your own, or have to use another solution for your specific problem. Please note, that std::atomic<glm::vec3> may not be TriviallyCopyable on your platform (because of the size it may nowhere be trivially copyable). This means, that a mutex is used to synchronize this. For a vector I am really sure, that there is no way to trivially copy it. So you may use a pair of vector and mutex instead.

What are the differences between g++ version 4.0.0.8 and 4.3.2?

What's the difference between g++ 4.0.0.8 and g++ 4.3.2? These two are the most common C++ compilers that I have seen used in various programming competitions.
I tried to google it, but found nothing.
It's really not such a 'huge list', considering you're interested in the C++ changes between the two.
4.0.0.8 is just a patch revision of 4.0, whose release notes are here: http://gcc.gnu.org/gcc-4.0/changes.html
4.3.2 is a patch revision of 4.3, whose release notes are here: http://gcc.gnu.org/gcc-4.3/changes.html
If you look at the differences between them, I think the following list covers the most important differences between GCC 4.0 and 4.3 that you would really care about.
GCC 4.3.2 has (including changes from GCC 4.1 and GCC 4.2):
Experimental support for the ISO C++0x standard (that's a link)
long long is now officially supported in C++ (though it was an extension provided in older GCC anyway)
Template extern is supported
Right angle brackets like you'd see in std::vector<std::vector<int>> are now supported (note the lack of a space between the two > at the end of the declaration).
experimental support for Variadic Template Arguments
Static assertions
and some others
More TR1 library support
<regex> (gcc 4.3), <random> (gcc 4.2), and <complex> (gcc 4.2)
C++ visibility handling has been overhauled. (GCC 4.2)
Restricted visiblity is propagated from classes to members, from functions to local statics, and from templates and template arguments to instantiations, unless the latter has explicitly declared visibility.
The visibility attribute for a class must come between the class-key and the name, not after the closing brace.
Attributes are now allowed for enums and elaborated-type-specifiers that only declare a type.
Members of the anonymous namespace are now local to a particular translation unit, along with any other declarations which use them, though they are still treated as having external linkage for language semantics.
An undocumented template extension was removed in GCC 4.2 (was deprecated in 4.1)
The (undocumented) extension which permitted templates with default arguments to be bound to template template parameters with fewer parameters has been removed. For example:
template <template <typename> class C>
void f(C<double>) {}
template <typename T, typename U = int>
struct S {};
template void f(S<double>);
is no longer accepted by G++. The reason this code is not accepted is that S is a template with two parameters; therefore, it cannot be bound to C which has only one parameter.
Additionally, check the porting to GCC 4.3 guide, where some important stuff is, like:
Backward compatible/deprecated headers have been removed.
Mostly this means a bunch of pre-standard headers have been removed, like <iostream.h> (use the standard <iostream>), <hash_map.h> (use <tr1/unordered_map>) <hashtable.h> (use <tr1/unordered_map> or <tr1/unordered_set> depending on needs), etc. Again, not a big deal.
Stricter enforcement of standards
For example, the signature on main's two-argument form must be correct.
No duplicate function parameter names
void func(int x, int x); // now an error
And there have been various bug fixes, as well as changes I don't think really affect how you'd write competition code (like removal/addition of new compiler flags), and removal of stuff like the <? and >? operators (ever used those?).
All in all I don't think the differences between the two will cause you much grief if you had to write code for both compilers.
You are wondering about the difference between two different version of g++? It's two common version of GNU C++ compilers. Different Linux releases may have different version of g++ shipped with it. Like my current CentOS 5.5 has g++ 4.1.2 with it.
For you feature details, check the link in you comments.
Since the 4.3.2 is newer then 4.0.0.8, it is more standard compliant. For details, you have to check documentation and release changes

void, VOID, C and C++

I have the following code:
typedef void VOID;
int f(void);
int g(VOID);
which compiles just fine in C (using gcc 4.3.2 on Fedora 10). The same code compiled as C++ gives me the following error:
void.c:3: error: ‘<anonymous>’ has incomplete type
void.c:3: error: invalid use of ‘VOID’
Now, this is something in external library and I would like the owner to fix that problem. So I have a question - does C++ standard forbids this construct? Could you give me a pointer/citation? The only thing I can recall is that function declaration with (void) to signal empty parameter list is deprecated in C++, but I don't understand why typedefed VOID does not work.
Yes, as far as i know the second declaration is invalid in C++ and C89, but it is valid in C99.
From The C99 draft, TC2 (6.7.5.3/10):
The special case of an unnamed parameter of type void as the only item in the list
specifies that the function has no parameters.
It's explicitly talking about the type "void", not the keyword.
From The C++ Standard, 8.3.5/2:
If the parameter-declaration-clause is empty, the function takes no arguments. The parameter list (void) is equivalent to the empty parameter list.
That it means the actual keyword with "void", and not the general type "void" can also be seen from one of the cases where template argument deduction fails (14.8.2/2):
Attempting to create a function type in which a parameter has a type of void.
It's put clear by others, notable in one core language issue report here and some GCC bugreports linked to by other answers.
To recap, your GCC is right but earlier GCC versions were wrong. Thus that code might have been successfully compiled with it earlier. You should fix your code, so that it uses "void" for both functions, then it will compile also with other compilers (comeau also rejects the second declaration with that "VOID").
gcc bugs. Edit: since it wasn't clear enough, what I meant was gcc 4.3.2 was compiling it due to bugs. See #32364 and #9278.
I just put your code in a .cpp file, and it compiled with no problems in VS2005, SUSE, Redhat, and Solaris, so I guess your specific gcc version does not approve of this.
Gal