Both GCC (5.3.0) and Clang (3.8.0) agree that this is valid code:
constexpr std::integral_constant<size_t, 0> n{};
std::get<n>(std::make_tuple(123));
However, they disagree on this:
std::integral_constant<size_t, 0> n;
std::get<n>(std::make_tuple(123));
Clang is ok with it, but GCC reports "the value of 'n' is not usable in a constant expression" / "'n' was not declared 'constexpr'".
Whose behaviour matches the standard?
Clang is right, although it ultimately depends on your library implementation. The standard does not per se disallow calling a constexpr function for a non-const(expr) object in constant expressions; only usage of that object's members would be a problem (see [expr.const]/(2.7.3)). Since the conversion operator most certainly simply returns 0, it's probably a GCC bug (also suggestive when considering the error message).
Related
Basic idea is this: I have some constexpr function and I want to use throw to signal error and lazy compilation to avoid this error in normal flow:
template <size_t N>
auto constexpr find_elt(const std::array<int, N>& a, int k) {
for (size_t i = 0; i < N; ++i)
if (k == a[i])
return i;
throw "not found";
}
And then:
constexpr int result = find_elt(arr, 4);
Normally, if 4 exists in the array, I will get its index back at compile-time.
If not, I will fall through to throw to indicate the lookup is erroneous at compile-time, and the compiler will produce a pretty error.
But I noticed strange behavior:
Under the latest clang, everything works
Under the latest gcc, everything fails
Is this idea legitimate? Is this code correct for what I want to achieve? Which compiler tells me the truth here?
If not, what is the correct way to do this?
Any links to C++ standard are appreciated. I read through constexpr-related chapters, but I am in doubt.
So:
according to the fact that the compilation of constexpr functions is a "lazy" process, then the check for compliance with the requirements for a constexpr function is performed only in the scope where the compiler still entered during expression substitution.
a function is a scope, respectively - all the rules for constexpr must be observed in the entire body of a function in its first level, scope.
and since the expression "throw" is not a constant expression (as the gcc-10 compiler already tells us about it), the correctness is not observed.
The clang compiler is not as strict in this sense as gcc. Therefore, in this battle, in my opinion, gcc wins. He is more committed to the Standard.
On the other hand, if this is a "lazy" process, then why shouldn't it be lazy to the end. Well, you found the final return - so why check the correctness further?
In this sense, clang gets a point.
And in the end - what does the C++17 Standard say?
10.1.5 The constexpr specifier [dcl.constexpr]
"... if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (8.20), ..., the program is ill-formed, no diagnostic required.
Next, let's see what the "core constant expression" is:
8.20 Constant expressions [expr.const]
An expression is a the evaluation of, following the rules of the abstract machine (4.6), would evaluate one of the following expressions:
2.22 - a throw-expression (8.17)
And note that "no diagnostic required" and the compiler is not required to provide a detailed explanation of the reason for the failure.
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.
The following code works without warning:
std::chrono::duration<unsigned int> d{-17};
I would expect the same warning I get from:
unsigned int x = -17;
Here's the relevant code:
template<typename _Rep2, typename = typename
enable_if<is_convertible<_Rep2, rep>::value
&& (treat_as_floating_point<rep>::value
|| !treat_as_floating_point<_Rep2>::value)>::type>
explicit duration(const _Rep2& __rep)
: __r(static_cast<rep>(__rep)) { }
The static_cast is hiding warnings, and it seems to me that it isn't required for any functionality the standard mandates. Is this just a libc++ problem, or is it required to work this way by the standard?
This is behavior as expected by the standard. The remarks on that constructor are:
This constructor shall not participate in overload resolution unless Rep2 is implicitly
convertible to rep and
(1.1) — treat_as_floating_point_v<rep> is true or
(1.2) — treat_as_floating_point_v<Rep2> is false.
int is implicitly convertible to unsigned int and treat_as_floating_point<int> is false, so we're fine.
The effects are:
Postcondition: count() == static_cast<rep>(r).
libc++ and libstdc++ are both conforming by allowing the code you wrote. It is well-formed. If you think it should be ill-formed, you should submit an issue about it. This isn't a compiler bug. It may be a standard bug.
I'm getting inconsistent results for std::is_constructible<void()>::value. My interpretation of the standard is that it should be false. However, Clang, with both libc++ and libstdc++*, gives true. GCC and MSVC both give false. Which result is correct?
Standardese
Here is the standardese, N4527 [meta.unary.prop]/7:
Given the following function declaration:
template <class T> add_rvalue_reference_t<T> create() noexcept;
the predicate condition for a template specialization
is_constructible<T, Args...> shall be satisfied if and only if the
following variable definition would be well-formed for some invented
variable t:
T t(create<Args>()...);
Note: This text changed slightly from C++11 (N3485), where create was not marked noexcept. However, the results of my tests did not change when accounting for this.
Test Case
Here is my minimal test case of both the type trait and the standardese definition:
#include <type_traits>
static_assert(std::is_constructible<void()>::value, "assertion fired");
template<typename T>
std::add_rvalue_reference_t<T> create() noexcept;
template<typename T, typename... Args>
void foo() {
T t(create<Args>()...);
}
int main() {
foo<void()>();
}
Results:
Clang (HEAD, libc++):
static assertion PASSED
foo<void()> did NOT compile
Clang (HEAD, libstdc++)*:
static assertion PASSED
foo<void()> did NOT compile
GCC (HEAD, libstdc++):
static assertion FAILED
foo<void()> did NOT compile
MSVC (version 19 via http://webcompiler.cloudapp.net/):
static assertion FAILED
foo<void()> did NOT compile (requires commenting out the static assertion)
*__GLIBCXX__ is not defined when Clang is used both with no -stdlib option and with -stdlib=libstdc++. I am unsure of whether libstdc++ is actually being used. If my interpretation of the standard is correct, then I am unsure of whether it is a bug with Clang or with libc++.
Keep reading. From the same paragraph:
Access checking is performed as if in a context unrelated to T and
any of the Args. Only the validity of the immediate context of the
variable initialization is considered. [ Note: The evaluation of the
initialization can result in side effects such as the instantiation of
class template specializations and function template specializations,
the generation of implicitly-defined functions, and so on. Such side
effects are not in the “immediate context” and can result in the
program being ill-formed. —end note ]
The assertion only fails when the template constructor is instantiated. However, as cleared up in the note, that assertion is not in the immediate context of the variable definition that is considered, and thus does not affect its "validity". So the compilers can count that definition as valid, even if actually attempting to construct a void() results in an ill-formed program.
Note that the compilers are also allowed to, instead of having is_constructible yield false, just reject the original program based on the assertion.
I was reading an article about non-type template arguments, and it said that :
When being instantiated, only compile time constant integer can be passed. This means 100, 100+99, 1<<3 etc are allowed, since they are compiled time constant expressions. Arguments, that involve function call, like abs(-120), are not allowed.
Example :
template<class T, int SIZE>
class Array{};
int main(){
Array<int, 100+99> my_array; // allowed
Array<int, abs(-120)> my_array; // not allowed
}
what's the difference between 100+99 and abs(-120) ?
how come 100+99 are compiled time and abs(-120) is not?
None, and abs(-120) is entirely legal in C++11. C++03, as you adequately point out, did not have scope for functions which could evaluate at compile-time, but C++11 does. For abs directly, you could replace it with a template which performs the same computation and use abs_template<-120>::value in C++03.
Edit: I meant to say that, even if abs was not constexpr, you could trivially write your own abs which is constexpr. Coulda sworn I edited that in.
100+99 is optimized out to 199 at compile time.
abs() is function and it may or may not be marked constexpr (C++11 feature, that would allow you to do so; you can easily check cppreference or standard to see if it's constexpr in C++11). It requires to be executed; compiler cannot deduce that it's state less function returning same value for every run with same argument.