I have here the case that a function could potentially be constexpr. Normally one adds the constexpr and use the constant evaluation only if the context allows it. However the following code complaints despite not using it in a constexpr context:
template <typename T>
struct Wrapper
{
friend constexpr bool operator==(const Wrapper& crLhs, const Wrapper& crRhs) noexcept
{
return crLhs.m_t == crRhs.m_t;
}
T m_t = {};
};
Using Visual Studio 2017 15.9.20 this gives 'error C3615: constexpr function 'operator ==' cannot result in a constant expression' when e.g. instantiated for std::string. The information is correct but I am not instantiating it in a constexpr context.
void f()
{
bool b;
Wrapper<int> a;
b = a == a; //ok
Wrapper<std::string> c;
b = c == c; //C3615, but not using constexpr context
}
I can apply a workaround it by using a member template or drop the constexpr but is there fancy trick here to have the best of both worlds (i.e. constexpr when applicable)?
Your code is fine. This is a bug in older versions of MSVC.
This bug was fixed in MSVC version 19.22, and compiles without error. Here we can see a side-by-side of the two compiler versions: https://godbolt.org/z/79kXFm
All versions after and including 19.22 compiles it, but Version 19.21 and below incorrectly give you error C3615, even though both of them are set to use C++11.
GCC and Clang never had this bug.
The bug was only ever in MSVC, and even very old versions of GCC and Clang compile the code without giving you an error.
What should you do?
If possible, you should just move to a newer version of Visual Studio. This is the simplest option to upgrade the compiler, and if you move to a newer version the compiler should receive bug fixes and upgrades. If that's not an option, I would google different ways to upgrade just the compiler itself. This might be helpful.
Related
I would expect the following code to produce a compiler error because the nested constraint within the requires clause is not a constant expression. However, Clang 15.0.0 seems to be okay with it and compiles it without any errors. This seems to be a regression compared to Clang version 14.0.0, which does produce the expected error. Is there a good reason for this change in behavior or is this simply a compiler bug of Clang?
struct s
{ static auto b() -> bool; };
template<typename T>
concept c = requires
{ requires T::b(); };
static_assert(not c<s>); // fails with Clang 14, GCC 12.2, and MSVC 19.32
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.
In this question:
Print template typename at compile time
we have a few suggestions regarding how to get typical C++ compilers to print a type's name, at compile time. However, they rely on triggering a compilation error.
My question: Can I get the C++ compiler to print the name of a type without stopping compilation?
In general the answer is "probably not", because a valid program can be compiled into its target object without printing anything anywhere, so I'm asking specifically about GCC and clang, with possible use of preprocessor directives, compiler builtins, or any compiler-specific trick.
Notes:
Obviously, the challenge is printing types behind using/typedef statements, template parameter values, variadic templates etc. If the type is available explicitly you could just use something like #message "my type is unsigned long long" (as #NutCracker suggested). But that's not what the question is about.
Answers relying on C++11 or earlier are preferred to requiring C++14/17/20.
gcc and clang offers some interface for using own plugins which can do nearly everything on different stages from parsing to code generation.
The interfaces are compiler specific and as this a plugin for gcc can not be used for clang or visa versa.
The documentation is havy and there is no chance to go in any detail here, so I only point you to the docs from gcc and clang:
gcc plugin
clang plugin
The following mechanism is due to #JonathanWakely, and is specific to GCC:
int i;
template <typename T>
[[gnu::warning("your type here")]]
bool print_type() { return true; }
bool b = print_type<decltype(i)>();
This gives you:
<source>:In function 'void __static_initialization_and_destruction_0(int, int)':
<source>:7:33: warning: call to 'print_type<int>' declared with attribute warning: your
type here [-Wattribute-warning]
7 | bool b = print_type<decltype(i)>();
| ~~~~~~~~~~~~~~~~~~~~~~~^~
See it working on Godbolt.
In c++17 we can abuse the [[deprecated]] attribute to force the compiler to issue a warning containing the desired template parameter:
template<typename T>
[[deprecated]] inline constexpr void print_type(T&& t, const char* msg=nullptr){}
print_type(999, "I just want to know the type here...");
The snippet above will print the following warning with gcc:
<source>:32:59: warning: 'constexpr void print_type(T&&, const char*) [with T = int]' is deprecated [-Wdeprecated-declarations]
print_type(999, "I just want to know the type here...");
In contrast to the accepted answer this will work with every c++17 compliant compiler. NB that you will have to enable \W3` on MSVC.
We can even go further and define a static assert macro that will print the type if and only if it fails.
template<bool b, typename T>
inline constexpr bool print_type_if_false(T&& t) {
if constexpr (!b)
print_type(std::forward<T>(t));
return b;
}
// Some nice static assert that will print the type if it fails.
#define STATIC_ASSERT(x,condition, msg) static_assert(print_type_if_false<condition>(x), msg);
Here is a live example.
Looks like gcc allowed calling non-const parent methods from const methods of template classes if the latter are not instantiated, but stopped doing this in version 7.1.0. The code below compiles using gcc 6.3.0, but gcc 7.1.0 fails with error: no matching function for call to 'B<T>::foo() const'.
struct A
{
int foo () { return 0; }
};
template<typename T>
struct B: public A
{
int bar() const { return foo(); }
};
int main ()
{
B<int>();
return 0;
}
I tested different versions here: https://wandbox.org/
If I remove the line template<typename T>, both versions fail with error: passing 'const B' as 'this' argument discards qualifiers [-fpermissive]
Please explain, why the earlier versions allowed such code, and the recent versions don't?
UPD: All the versions of clang and msvc I found compile the example. So it is wasn't some specific feature of bug in earlier versions of gcc.
UPD: The problem is apparently that earlier versions didn't check non-instantiated methods of template classes. But now they do. Why they did it?
Code sample:
class A
{
static constexpr auto GetInt() noexcept { return 6; }
template<int N>
std::enable_if_t< N >= GetInt(), int> func() { return N; }
};
https://godbolt.org/z/-0pwIQ
Clang and MSVC both claim that GetInt() can't be used because it's not defined at that point, however GCC compiles with no errors or warnings.
My best guess for why the error occurs is that because the class is incomplete at the point that func(), member functions are considered undefined, and because auto relies on the function definition to deduce the return type, the compiler can't use it to generate a function signature.
However, that doesn't explain why GCC is allowing it. Is it incorrect to do so?
As it is already mentioned in the comments, this is one of C++ Standard Core Language Active Issues: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2335
It is still under discussion.
Though it does look like Clang/MSVC behavior might become standard:
Notes from the June, 2018 meeting:
The consensus of CWG was to treat templates and classes the same by
"instantiating" delayed-parse regions when they are needed instead of at the
end of the class.