I have a simple constexpr class (using c++17)
struct foo {
float x, y;
// ill formed constexpr in vs 2015
foo() {}
constexpr foo(float x, float y) : x(x), y(y) {
}
};
constexpr auto bar() {
return foo(4.0, 5.0);
}
int main() {
auto f = bar();
}
This is illformed bc of the non constexpr default constructor on 2015. But vs 2019 doesnt report a problem.
According to this reference page "all selected ctors" must be constexpr. I assume the word "selected" means "used" and even though default ctor is unused, 2015 wont eval into a constexpr until I make the default ctor constexpr.
Other specs are worded differently but I could not make clear of them either. Also most constexpr examples use the keyword everywhere. An example of selective constexpr use would be nice.
Latest GNU ans Clang behave like vs 2019 but Im not convinced that this was an oversight or bug in vs 2015.
So what is it? Should all ctors be constexpr or is visual studio 2019 correct here?
There has never been a requirement that all constructors of a literal type must be constexpr. The requirement has only been that all functions which get invoked in a constant expression context must be able to be constant expressions (and thus must be declared constexpr).
Note that older versions of Visual Studio did not do a good job of implementing constexpr correctly.
Related
I was playing around with constexpr when I found that GCC rejected this seemingly valid code:
#include <functional>
constexpr void test(const std::function<void()>& a) {
a();
}
int main() {
test([](){});
}
I went to godbolt, and clang happens to compile this code fine. Is this a bug in GCC? Here is the godbolt link
As already mentioned in the comments, std::function itself can not be used in constexpr environemt, because the operator()() and also the constructor of std::function is not constexpr.
As this you can use directly a pointer to function, if you have capture less lambdas or you can templatize you function like this in C++20:
constexpr void test(auto&& a) {
a();
}
or with explicit template parameters in older C++ standards.
Use auto or auto& or auto&& as needed to allow temporary lambda, move it in or copy it ( which might be the same after optimizing )
Taken from the comments:
A constexpr function must have at least one set of inputs that are able to be evaluated in a constant expression - otherwise it's ill formed no diagnostic required*
As clang did not report something did not mean it is a clang bug.
Visual C++ is notorious for ignoring constexpr function qualifiers unless absolutely required. Look at the following function:
constexpr int multiply(int l, int r) noexcept
{
return l * r;
}
According to the standard, Visual C++ is completely allowed to not evaluate the rvalue at compile-time:
auto three_times_four = multiply(3, 4);
The workaround I've been using is this ugly force:
constexpr auto constexpr_three_times_four = ;
auto not_constexpr_three_times_four = constexpr_three_times_four;
// use not_constexpr_three_times_four in non-constexpr contexts
// alternatively:
template<auto val>
inline constexpr auto ensure_constexpr = val;
auto not_constexpr_three_times_four = ensure_constexpr<multiply(3, 4)>;
Is there a way I can hint to the compiler that these things should be evaluated at compile-time?
I'm especially annoyed with the following:
namespace l
{
constexpr ::std::uint32_t operator""_crc32(const char * p, ::std::size_t const size) noexcept
{
return crc32(p);
}
}
//...
using namespace l;
search("foo"_crc32);//You don't want to evaluate this at runtime? too bad.
So, what can I do to hint the compiler in this case and avoid these ugly fixes?
There is no mechanism to "hint" to the compiler (any compiler) that a constexpr function "should" be called at compile-time. That's not what constexpr is for. It's not a tool for speeding up execution of code. It's a tool for allowing you to do computations that have to be done at compile-time.
C++20 allows functions to be designated consteval, which ensures that the function must be executed within a constant expression. But even that feature isn't for performance; it's there so that they can add new features of the language (like reflection values) that can only exist at compile time and cannot leak into runtime code.
C++20's constinit allows you to declare non-constant expression variables whose initializer is required to be a constant expression. That's the closest C++ gets to constexpr-as-a-performance-feature.
But otherwise, if the compiler's higher optimization levels aren't calling those functions at compile time, then that's how the compiler has chosen to implement the feature.
Visual Studio 2015 Update 3.
I read the Programming. Principles and Practice Using C++ (second edition) by Bjarne Stroustrup. I learn the constexpr functions...
It works:
constexpr int get_value(int n) {
return n + 1;
}
But I can't compile this (instead of the first variant):
constexpr int get_value(int n) {
return ++n;
}
I get the error:
constexpr function return is non-constant
The n is the local variable for the get_value function. I.e. n variable changing doesn't influence to external code.
Why the second variant of the get_value function is wrong?
The second one is not allowed in C++11 constexpr. The standard even had a very similar example (N3337 [dcl.constexpr]/3):
constexpr int prev(int x)
{ return --x; } // error: use of decrement
N3337 [expr.const]/2 explicitly bans "increment or decrement operations" in constant expressions.
C++14 extended constexpr relaxes those requirements, but MSVC doesn't implement that.
The second one is legal under C++14 but it doesn't compile because Visual Studio 2015 has only partial support for constexpr functions. It only has support for single-return constexpr functions and other limitations (like yours), which where in effect in C++11.
See this article (in the constexpr paragraph). Visual Studio "15" will bring improvements to constexpr functions. You'll have to wait a bit :)
The following code, a simplified version of code found in the googlemock project, fails to compile in Visual Studio 2015 Update 1, but it compiles on clang [Apple LLVM version 7.0.0 (clang-700.1.76)].
struct ConvertibleFromAny
{
ConvertibleFromAny(int a_value);
template <typename T>
ConvertibleFromAny(const T& a_value);
};
template <typename T>
struct Matcher
{
Matcher(T value);
};
template <typename Rhs>
struct EqMatcher
{
explicit EqMatcher(const Rhs& rhs);
template <typename Lhs>
operator Matcher<Lhs>() const;
};
int main()
{
EqMatcher<ConvertibleFromAny> em(1);
Matcher<ConvertibleFromAny> m = em;
return 0;
}
The error occurs at the assignment statement
Matcher<ConvertibleFromAny> m = em;
and the error message is
error C2440: 'initializing': cannot convert from 'EqMatcher<ConvertibleFromAny>' to 'Matcher<ConvertibleFromAny>'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
I can naively see an ambiguity between a member call to
EqMatcher<ConvertibleFromAny>::operator Matcher<ConvertibleFromAny>()
and an initialization conceptually similar to
Matcher<ConvertibleFromAny> m(ConvertibleFromAny<EqMatcher<ConvertibleFromAny>>(em))
My guess is that clang rules out the second option.
EDIT: Inspired by T.C.'s comment I tested the following:
struct A
{
};
struct X
{
X(const A&);
};
struct B
{
B(const X&);
};
int main()
{
A a;
B b = a;
}
It compiles with VS 2015, but not with clang. I have not been able to find any references that documents that the Visual C++ implementation intentionally deviates from the standard in this regard.
Is this a well-known problem?
Both your code samples produce the expected result with VS2015 Update 1, if I enable the "Disable Language Extensions" (/Za) flag. That is, the first one compiles, the second one does not.
I'm not sure which extension in particular is interfering here, though.
I found this MSDN page: Microsoft Extensions to C and C++, but it does not appear to be complete - for example, binding a non-const T& to an rvalue is not mentioned.
I have not been able to find any references that documents that the
Visual C++ implementation intentionally deviates from the standard in
this regard.
Here you go: Compiler Warning (level 1) C4928. The message is
illegal copy-initialization; more than one user-defined conversion has been implicitly applied
It also says this:
The compiler executed the code in all such routines.
So there's a de facto language extension that Microsoft has only barely documented.
You can use command line argument /we4928 to convert the warning into an error, effectively removing this single extension. See here for these arguments.
struct A {
int a = 0;
constexpr A() { a = 1; }
};
constexpr bool f() {
constexpr A a;
static_assert(a.a == 1, ""); // L1: OK
return a.a == 1;
}
static_assert(f(), ""); // L2: Error, can not modify A::a in constexpr
Online Compiler URL: http://goo.gl/jni6Em
Compiler: clang 3.4 (with -std=c++1y)
System: Linux 3.2
If I delete L2, this code compiles. If I add L2, the compiler complained "modification of object of const-qualified type 'const int' is not allowed in a constant expression". I am not a language lawyer, so I am not sure whether this is true. However, if it is, why compiler didn't complain anything about L1, since it also called A() as constexpr? Is this a bug of clang? Or did I miss anything?
Reference: http://en.cppreference.com/w/cpp/language/constexpr
BTW, if I change "constexpr A a;" to "A a;" (remove constexpr keyword), L1 failed to compile which is expect. However, the compiler didn't complain about L2 anymore.
Online Compiler URL about this: http://goo.gl/AoTzYx
I believe this is just a case of compilers not having caught up to the changes proposed for C++14. Your constexpr constructor satisfies all the conditions listed in ยง7.1.5/4 of N3936. Both gcc and clang fail to compile your code, but for different reasons.
clang complains:
note: modification of object of const-qualified type 'const int' is not allowed in a constant expression
which doesn't make much sense, but reminds me of the C++11 restriction that constexpr member functions are implicitly const (this is a constructor, and that doesn't apply, but the error message is reminiscent of that). This restriction was also lifted for C++14.
gcc's error message is:
error: constexpr constructor does not have empty body
Seems pretty clear that gcc still implements the C++11 rules for constexpr constructors.
Moreover, N3597 lists this example:
struct override_raii {
constexpr override_raii(int &a, int v) : a(a), old(a) {
a = v;
}
constexpr ~override_raii() {
a = old;
}
int &a, old;
};
N3597 was superseded by N3652, which contains the wording found in the current draft. Unfortunately, the earlier example disappears, but, again, nothing in the current wording says you cannot assign values to data members within the body of a constexpr constructor.
Update (2017-10-03)
clang fixed this, but there has been no new release yet: https://bugs.llvm.org/show_bug.cgi?id=19741
(Compiler explorer)