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
Related
The discussion first takes place at llvm/llvm-project#60377. After reading temp.constr.atomic and temp.constr.normal again and again, I feel even more confused about this question beacause of my poor English.
Like the example below, in order to help clang-format format requires clause correctly, I used to add a pair of bracket. Now I changed my style. When I tried to change my code, the unexpected error happened.
#include <concepts>
template <typename T>
requires std::integral<T>
struct [[nodiscard]] Widget;
template <typename T>
requires(std::integral<T>) // error: Requires clause differs
struct [[nodiscard]] Widget {
T value;
};
[[nodiscard]] auto main() -> int {
Widget<int> widget{1};
return widget.value;
}
I tested this with different compilers. It turns out:
error - x86_64 clang (trunk)
pass - x86_64 gcc (trunk)
error - x64 msvc v19.latest
What is the expected behaviour according to the standard, clang/msvc or gcc?
What is the expected behaviour according to the standard, clang/msvc or gcc?
The standard does not require any particular behaviour (C++23 CD [intro.compliance.general] paragraph 2). The program is ill-formed, no diagnostic required because the validity of the program depends on whether or not the template-heads are equivalent ([basic.link] paragraph 11) and they are functionally equivalent but not equivalent ([temp.over.link] paragraph 7).
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.
GCC (8.3, 9.1), Clang (7, 8) and MSVC (19.20) differ is their ability to compile this code:
struct C;
template<typename T> struct S {
void foo() {
// C2 c;
C c;
}
};
class C {};
int main() {
S<int> s;
s.foo();
return 0;
}
GCC and MSVC accept it, whereas Clang rejects it. Clang rejects it even if I make foo itself a template and/or do not call it at all.
My understanding is that foo is not instantiated unless it is called, and it is instantiated at the point where it is called. At that point C is complete, and the code should compile. Is this a reasoning of GCC?
As a side note, if foo is not called, MSVC accepts the code even if I replace C with an undeclared C2 inside foo - in this case it seems to just check the function body to be syntactically correct.
Which behaviour is correct according to the Standard? If it is Clang's, why does the Standard forbids the flexibility that GCC gives?
This is ill-formed, no diagnostic required, due to [temp.res]/8:
The program is ill-formed, no diagnostic required, if:
[...]
a hypothetical instantiation of a template immediately following its definition would be ill-formed due to a construct that does not depend on a template parameter, or [...]
[...]
So the whole program is bad, but implementations aren't required to diagnose it. Clang does, which is good for clang, gcc and MSVC don't, which isn't wrong.
A simplified example from a recent blog post:
struct B { void f(); };
struct D : B { };
constexpr auto as_d = static_cast<void(D::*)()>(&D::f); // (1)
template <void (D::*)()>
struct X { };
X<as_d> x; // (2)
gcc, clang, and MSVC all accept the declaration of as_d marked (1). gcc and clang both reject the declaration of x marked (2), but MSVC accepts it.
Both gcc's and clang's error messages indicate that they know that as_d is a pointer to member of B. clang:
<source>:9:3: error: sorry, non-type template argument of pointer-to-member type void (D::*)() that refers to member B::f of a different class is not supported yet
gcc:
<source>:9:7: error: void (D::*)(){((void (D::*)())B::f), 0} is not a valid template argument for type void (D::*)()
Who is right? If gcc/clang, what is the rule we're running afoul of? It sure seems like as_d is a converted constant expression of type void (D::*)() to me...
Well, this ended up being pretty interesting. As far as the language is concerned, the program is valid - as_d does meet the requirement for being a valid non-type template argument (it is a converted constant expression of the right type).
However, the Itanium C++ ABI apparently does not specify a mangling for this situation (that is, having a non-type template argument whose type is a pointer-to-member-to-derived but whose value is a pointer-to-member-to-base). Compilers targeting that ABI (i.e. clang and gcc), as a result, can't accept this code. This explains why clang's error is "sorry, not yet" rather than "no, bad!"
On the other hand, other ABIs have no such mangling problem, and so MSVC and ICC are both able to compile the program just fine.
This question already has answers here:
Injected class name compiler discrepancy
(3 answers)
Closed 7 years ago.
I was playing around with templates today to see if I could get the compiler to deduce the type of an outer class from one of its inner classes.
I didn't find my solution (which I suspect is impossible), but while trying to fix an error I ran into very strange behavior that I reduced to the following snippet.
struct A
{
struct B{};
template <typename T>
struct EverythingIsFine
{
using Outer = T;
using Inner = typename T::B::B::B::B::B::B;
};
using ItWillBeOkay = EverythingIsFine<B>; // Probably not ok
using InnerProblem = ItWillBeOkay::Inner; // Still not ok
using OuterProblem = decltype(B().ItWillBeOkay::Outer::B::B::B
::B::B::B::~B()); // Not even CLOSE to ok
};
It surprisingly compiles with no warnings and no errors with both Clang and GCC.
The versions of my compilers are gcc version 5.3.1 20160121 (Debian 5.3.1-7) and Debian clang version 3.6.2-3 (tags/RELEASE_362/final) (based on LLVM 3.6.2) and the flag used to compile are -std=c++11 -Wall -Wextra.
I observed that it also compiles fine on Ideone with the C++14 setting.
I then used this simple test to get the exact types of InnerProblem and OuterProblem:
template <class T> void Type();
int main()
{
Type<A::InnerProblem>();
Type<A::OuterProblem>();
}
And both compilers report the same types when compiling the test:
In function main:
main.cpp:20: undefined reference to void Type<A::B>()
main.cpp:21: undefined reference to void Type<void>()
That is to say, the type of InnerProblem is A::B and the type of OuterProblem is void.
Is this somehow permitted by the standard or is it a bug in both compilers?
And since I seem to be as confused as my compiler, what is actually happening with this code?
EDIT: As a simplified followup, because I don't understand why two compilers can not give the same result, the following code compiles with Clang, but not with GCC.
struct A
{
struct B{};
template <typename T>
struct EverythingIsFine
{
using Inner = typename T::B::B::B;
};
using Problem = EverythingIsFine<B>::Inner::B::B::B; // Not ok
};
GCC now outputs the following error:
main.cpp:11:26: error: 'A::B::B' names the constructor, not the type
using InnerProblem = EverythingIsFine::Inner::B::B::B; // Not ok
It's valid.
The class-name is also inserted into the scope of the class itself;
this is known as the injected-class-name." (9/2).
So B::B names the class B, as does B::B::B, and so on.
EDIT:
So typename B::B names the class B, as does typename B::B::B, and so on.