Processing of uninstantiated template functions - c++

The following code compiles in Visual C++ 2013 but not under G++ 4.8.2:
template<class T>
int MyFunc(T& t)
{
return static_cast<int>(CCodes::blah);
}
template<>
int MyFunc(float& t)
{
return 0;
}
int main() {
float f = 10.f;
return MyFunc(f);
}
Visual C++ seems to ignore the general template function because only the specialisation MyFunc<float> is used. G++ parses the general function anyway and spots that the CCodes enumeration has not been defined.
Which is right? Or is this implementation-defined?

GCC is correct, and every other compiler besides MSVC will do the same thing.
This is a major bug, which actually appeared on one MSVC future roadmap. It was in the "distant future" category. They will have to rewrite their template engine to fix it.
There is a line of argument that diagnosis of an ill-formed template is optional, because it's really a template with no well-formed instantiation, and those are not required to be flagged. However,
The standard requires the template to be parsed, and failure to parse must be diagnosed regardless of instantiation.
Every other compiler makes the diagnosis, so in effect not doing so leads MSVC users to produce unportable code. Complaining is a really good idea, even if it's not required.

Related

Template class compiles with C++17 but not with C++20 if unused function can't be compiled

The following compiles without error in VS2019 (version 16.11.15) with C++ 17 selected as the language. But it fails with C++ 20 with error "error C2027: use of undefined type 'Anon'"
template <typename T> class a_template
{
public:
void do_something(class Anon& anon) const;
};
template <typename T> void a_template<T>::do_something(class Anon& anon) const
{
anon.do_something();
}
The Anon class is of course undefined but the ::do_something function is unused so does not need to be instantiated. This is OK in C++17 but apparently not in C++20.
Is this a change in language rules? If so, can it be fixed without actually defining Anon?
Is this a change in language rules?
No, this is due to the fact that a C++ compiler is permitted (but not required!) to diagnose errors at the time the template is parsed
when all of the instantiations of the template would produce that error.
This means that for your given example, at the time of parsing the definition compilers may or may not issue an error. That is, the compiler can produce an error when parsing the template or may wait until the first template instantiation. Refer to demo where msvc doesn't issue an error but gcc and clang does.
Perhaps a simpler example would make it more clear :
void func()
{
}
template<typename T> void bar()
{
func(3); //compilers are allowed(but not required) to issue error at the time of pasrsing this
}
In the above example, func is a nondependent name and at the point where we have called func using func(3), the only visible func is the one that accepts 0 arguments and not one. And as i said earlier, some compiler may issue an error(at the time of parsing) even though we've not instantiated bar but some compilers may not. This is because they are allowed to but not required to do so. See demo where msvc doesn't issue an error here but gcc and clang does.
The same logic applies to your example as well. Meaning as Anon is an incomplete type and you have anon.do_something(), some compiler might choose to produce an error even though you have not instantiated a_template and some other compiler might not.

What should `foo.template bar()` do when there's both a template and a non-template overload?

A coworker shared this code with me:
run on gcc.godbolt.org
#include <iostream>
struct A
{
void foo() {std::cout << "1\n";}
template <typename T = int>
void foo() {std::cout << "2\n";}
};
int main()
{
A x;
x.template foo();
}
GCC prints 1, Clang prints 2, and MSVC complains about missing template arguments.
Which compiler is correct?
[temp.names]/5 says that a name prefixed by template must be a template-id, meaning that it must have a template argument list. (Or it can refer to a class/alias template without template argument list, but this is deprecated in the current draft as a result of P1787R6 authored by #DavisHerring.)
There is even an example almost identical to yours under it, identifying your use of template as ill-formed.
The requirement and example comes from CWG defect report 96, in which the possible ambiguity without the requirement is considered.
Open GCC bug report for this is here. I was not able to find a Clang bug report, but searching for it isn't that easy. Its implementation status page for defect reports however does list the defect report as unimplemented.
MSVC is correct to reject this: the standard has just this as an example. The template parser guide is allowed before the qualified name of a class or alias template without template arguments, but this is only for compatibility with implementations that needlessly require it for template template arguments and is now deprecated.

std::abs can be used in constexpr function, but only if it's templated. Why?

Supposedly std::abs is not constexpr in the standard (even in C++20). But in practice I found out that I can compile it as constexpr under the very peculiar condition that the function is templated. See this completely working example:
template<class T>
constexpr T f(const T input) {
return std::abs(input);
}
int main() {
int i = -1;
int a = f(i);
return 0;
}
The code:
Compiles fine with GCC, with and without the template.
It doesn't work in Clang.
And in Visual Studio it compiles with the template line, but fails compilation without the template.
For a regular function the compiler may know, based on the type of the function parameters, if the inner code can be potentially evaluated in compile time. This is why you get an error for calling std::abs in MSVC and clang. The behavior of gcc is based on its decision to implement std::abs as constexpr which is by the way a questionable decision.
For a template function the compiler cannot know if the inner code can be evaluated in compile time, as it may be based on the actual type of the template arguments, with different functions overload being called. While most compilers would decide not to check whether all possible overloads of std::abs cannot be constexpr, thus letting the code pass compilation, theoretically a compiler may check (in very specific cases that can be checked, like this one) and since the user is not allowed to extend std by adding a new version of abs (the list of allowed extensions to std is closed by the spec) it is possible to see that the function can never be constexpr and thus to generate a compilation error. In the more general case however, the compiler cannot check for a template function if all possible cases cannot produce a constexpr function, since it sees only the available overloads for the inner call, per each call to the template function, and there might be other available overloads for the inner call, when the template is called elsewhere.
Note that making a constexpr function a template, just so it can get compiled, would not be a good approach. The actual decision if the function is constexpr (i.e. can be called in compile time) would be based on the actual call, and if in all cases the function cannot be constexpr you are trying in a way to cheat the compiler but eventually are cheating mainly yourself...
By the way, in my check with clang 10.1 and trunk versions, I don't get compilation error on the template version, this code compiles both with gcc and clang:
template<typename T>
constexpr T myabs(T t) {
return std::abs(t);
}
int main() {
int i = myabs(3);
}
While this compiles with gcc (which implements std::abs as constexpr) and fails with clang:
int main() {
constexpr int i = myabs(3);
}
It seems that both gcc and clang do not generate an error even if the inner call inside a constexpr template function is not dependent on the template parameters and can never be a constant expression:
int myabs() {
return 42;
}
template<class T>
constexpr int f() {
// this is never a contexpr
// yet gcc and clang are ok with it
return myabs();
}
And again, this is allowed as no diagnostic is required for non-conforming constexpr template functions:
[dcl.constexpr] 9.2.5/7 - The constexpr and consteval specifiers:
[...] If no specialization of the template would satisfy the requirements for a constexpr function when considered as a non-template function, the template is ill-formed, no diagnostic required.

Does -Werror interfere with template correctness and/or SFINAE?

I have the feeling that this is a dumb question and I guess that the answer is a simple "No", though I have no clue how to be certain about it other than asking for your help...
Does -Werror interfere with template correctness (not sure what is the right term, see example below) and/or SFINAE?
Consider this simple contrived example:
template <typename T>
void foo() {
int a;
}
int main() {
//int a; // error: unused variable 'a' [-Werror=unused-variable]
}
Uncommenting the line in main results in an error when compiled with -Werror. I know the compiler is supposed to generate an error for templates that are erroneous for any template parameter even if not instantiated. This is not the case here. I will only see the error (which is of course actually only a warning) here when I instantiate the template.
Why I ask this question: I am used to compile with -Werror always, hence my perception of what is a warning and what is an error is a bit blurry in some regards. Now for templates and especially SFINAE it does make a big difference if something is just a warning or really an error.
I know the compiler is supposed to generate an error for templates that are erroneous for any template parameter even if not instantiated.
That is not the case, though. If no instantiation can be generated for a template, then the program is ill-formed, no diagnostic required(1). So the program is ill-formed regardless of whether you get an error or it compiles "successfully."
Looking at it from the other perspective, a compiler must not allow a warning-turned-error to affect SFINAE, as that could change the semantics of a valid program and would thus make the compiler non-conforming. So if a compiler wants to diagnose a warning as an error, it must do this by stopping compilation and not by introducing a substitution failure.
In other words, -Werror can make the compiler reject a well-formed program (that is its intended purpose, after all), but it would be a compiler bug if it changed the semantics of one.
(1) Quoting C++17 (N4659), [temp.res] 17.6/8:
The program is
ill-formed, no diagnostic required, if:
no valid specialization can be generated for a template ... and the template is not instantiated, or
...
While it's largely a quality of implementation issue, -Werror can indeed (and does) interfere with SFINAE. Here is a more involved example to test it:
#include <type_traits>
template <typename T>
constexpr bool foo() {
if (false) {
T a;
}
return false;
}
template<typename T, typename = void> struct Check {};
template<typename T> struct Check<T, std::enable_if_t<foo<T>()>> {};
int main() {
Check<int> c;
}
The line T a; can trigger that warning (and error), even though the branch is dead (it's dead on purpose, so that foo is a constexpr function mostly regardless of T). Now, according to the standard itself, that is a well-formed program.
But because Clang and GCC cause an error there, and that error is in the non-immediate context of the Check specialization, we get a hard error. Even though according to the standard itself this should just fall back to the primary template due to substitution failure in the immediate context only.

Double template<> in template specialization

Why does the code below compile? I am not specializing a template member function of a template class, so only one template<> should be used. However, g++ compiles it with no warnings whatsoever, clang++ gives only a warning
warning: extraneous template parameter list in template
specialization
template<typename T>
struct S{};
template<> template<> // why can we do this?
struct S<int>{};
int main()
{
}
Because the grammar allows it, and there doesn't seem to be anything under the template specialization section that prohibits it:
From [gram.temp]
explicit-specialization:
template < > declaration
From [gram.dcl]
declaration:
[...]
explicit-specialization
The fact that the grammar is too lax has been in the active issues list (#293) since 2001.
A bug report (filed as PR5559) from a much older version of clang discusses the issue as well. The problem is that gcc and clang both have discrepancies when it comes to whether multiple template declarations are valid during an explicit specialization. Quoth Gabor Greif:
The first error is actually none, clang correctly diagnoses that only one "template <>" is needed. But because g++ accepts this and several people (like me) may have the misconception that the number of "template <>"s is governed by nesting instead of the number of levels being specialized, it may be interesting to reduce the error to a warning and possibly emit a fixit hint.
The disparity could also be caused by the standard's cyclic definition of an explicit specilization (as noted by #user657267).