i have a constexpr function named access, and i want to access one element from an array:
char const*const foo="foo";
char const*const bar[10]={"bar"};
constexpr int access(char const* c) { return (foo == c); } // this is working
constexpr int access(char const* c) { return (bar[0] == c); } // this isn't
int access(char const* c) { return (bar[0] == c); } // this is also working
i get the error:
error: the value of 'al' is not usable in a constant expression
why can't i access one of the elements from access? or better how do i do it, if it is even possible?
The array needs to be declared constexpr, not just const.
constexpr char const* bar[10]={"bar"};
Without that, the expression bar[0] performs an lvalue-to-rvalue conversion in order to dereference the array. This disqualifies it from being a constant expression, unless the array is constexpr, according to C++11 5.19/2, ninth bullet:
an lvalue-to-rvalue conversion unless it is applied to
a glvalue of literal type that refers to a non-volatile object defined with constexpr
(and a couple of other exceptions which don't apply here).
Related
Maybe the title is not clear, so concretely:
#include <type_traits>
template<typename T>
constexpr int test(T)
{
return std::is_integral<T>::value;
}
int main()
{
constexpr int a = test(1); // right
constexpr int b = test(1.0); // right
int c = 2;
constexpr int d = test(c); // ERROR!
return 0;
}
In fact, the function doesn't use anything but the type of the parameter, which can be determined obviously in the compilation time. So why is that forbidden and is there any way to make constexpr get the value when only the type of parameter is used?
In fact, I hope to let users call the function through parameters directly rather than code like test<decltype(b)>, which is a feasible but not-convenient-to-use way, to check if the types of parameters obey some rules.
Just take T by reference so it doesn't need to read the value:
template<typename T>
constexpr int test(T&&)
{
return std::is_integral<std::remove_cvref_t<T>>::value;
}
You can even declare test with consteval, if you want to.
(Note that stripping cv-qualifiers isn't necessary in this instance; cv-qualified integral types satisfy the std::is_integral trait.)
Why can't constexpr be used for non-const variables when the function only uses the types?
Because the call expression test(c) is not a constant expression and hence it cannot be used as an initializer for d.
Basically, for the call expression test(c) to be a constant expression, c must be a constant expression. It doesn't matter whether c is used or not inside the function itself. This is why, the call expressions test(1) and test(1.0) works and can be used as an initializer for a and b respectively.
In C++ Primer, Fifth Edition, §6.5.2:
A constexpr function is defined like any other function but must meet certain restrictions: The return type and the type of each parameter in must be a literal type (§2.4.4, p. 66), and the function body must contain exactly one return statement
but another sentence in this chapter (page 239):
A constexpr function is permitted to return a value that is not a constant
// scale(arg) is a constant expression if arg is a constant expression
constexpr size_t scale(size_t cnt) { return new_sz() * cnt; }
Is it a contradictory summary? I am confused about it.
The return type of scale is literal type?
update:
what's the difference between literal type and constant ?
First of all what I believe the author meant was that a constexpr function does not have to result in a constant expression, which is an expression that can be evaluated at compile time.
A constexpr function will only yield a constant expression if the arguments to the function are also constant expressions and the comment right after says exactly that:
// scale(arg) is a constant expression if arg is a constant expression
and the examples that follow right after also demonstrate this behavior:
int arr[scale(2)]; // ok: scale(2) is a constant expression
int i = 2; // i is not a constant expression
int a2[scale(i)]; // error: scale(i) is not a constant expression
In C++(as opposed to C99) as array size must be a constant expression and so the last case is an error since the argument to scale is not a constant expression.
This is different concept from the return type of the function, which must be a literal type which is any of the following:
void(since c++14) (so that constexpr functions can return void)
scalar type which includes Arithmetic types, enumeration types, pointer types, pointer to member types, std::nullptr_-
t, and cv-qualified versions of these types)
reference type
an array of literal type
class type that has all of the following properties:
has a trivial destructor,
is either
an aggregate type
a type with at least one constexpr (possibly template) constructor that is not a copy or move constructor
all non-static data members and base classes are of non-volatile literal types.
It's not contradictory. As well as mandating that the return type must be of "literal type", the draft standard states that a call to a constexpr function does not have to appear in a constant expression. From the C++11 draft standard:
§7.1.5/7 A call to a constexpr function produces the same result as
a call to a equivalent non-constexpr function in all respects
except that a call to a constexpr function can appear in a constant
expression.
constexpr does nothing but tells the compiler that the value is there in compile time, so you can use it as template argument (for example)
int a1 = 5;
std::array<int, a1> arr1; // error, a is variable
const int a2 = 5;
std::array<int, a2> arr2; // OK
int f1() { return 3; }
std::array<int, f1()> arr3; // error, compiler doesn't know it is const 3
constexpr int f2() { return 3; }
std::array<int, f2()> arr4; // OK
Later you also can:
constexpr int f3() { return f1() + 1; } // error, f1 is not constexpr
constexpr int f4() { return f2() + 1; } // OK
std::array<int, f4()> arr5; // OK
Now about literal types limitation: the function arguments and result types should be literal types (Need clarification on definition of literal type), the very same limitation as template arguments apply (known in compile types).
constexpr std::string f5() { return "hello"; } // error,
// std::string is not literal type
constexpr const std::string& f6() {
static const std::string s = "hello";
return s;
}
template<const std::string& s> SomeClass { ... };
SomeClass<f6()> someObject;
Let's suppose we have a template function with non-type parameter of const char * like this:
template <const char * MESSAGE> void print() {
std::cout << MESSAGE << '\n';
}
Using this template wouldn't be a problem as log as the MESSAGE can be deduced at compile-time, so the following uses are legal:
namespace {
char namespace_message[] = "Anonymous Namespace Message";
constexpr char namespace_constexpr_message[] = "Anonymous Namespace Constexpr Message";
}
char message[] = "Message";
constexpr char constexpr_message[] = "Constexpr Message";
int main()
{
print<namespace_message>();
print<namespace_constexpr_message>();
print<message>();
print<constexpr_message>();
return 0;
}
But the ones below are not (see here):
namespace {
const char namespace_const_message[] = "Anonymous Namespace Const Message";
}
const char const_message[] = "Const Message";
int main()
{
print<namespace_const_message>();
print<const_message>();
print<"Literal">();
return 0;
}
The errors generated by the code above are the following:
the value of '{anonymous}::namespace_const_message' is not usable in a constant expression
I don't get why namespace_const_message is not usable in a constant expression while namespace_message is; if I must bet for one of them to be unable to be used in a constant expression I'll bet for the no constant one, but is the one which already works as constant expression!
note: '{anonymous}::namespace_const_message' was not declared 'constexpr'
namespace_message was neither declared as constexpr and is used into a constant expression and its value is deduced at compile time. Why constexpr is needed if the expression is const and not required if no-const?
Same goes for the values outside the anonymous namespace, I was trying to force the compile-time-constness placing the values into a internal linkage space but is obvious that I've failed.
Finally, the last error:
'"Literal"' is not a valid template argument for type 'const char*' because string literals can never be used in this context
So, surprisingly (at least it was a surprise for me) a string literal cannot be used as template argument, but as long as the string (well, a pointer to a null-terminated array of characters) is a compile-time value it can be used as non-type template parameters so: they're available at compile-time as long as "they are a lvalue" (but they're already lvalues!).
I'm trying to guess why a string literal can never be used in this context, and my best guess is that two string literals with the same content aren't the same literal (because the pointer which points to the content could be different) while two integral literals are the same (they're a value, not a pointer to a value).
So, what's the question here?
Why the namespace_const_message and const_message aren't available at compile-time and thus forbidden in the print template function?
Is my guess about the string literals correct?
Thanks.
The instantiation variable of a template needed to have external
linkage, and const was implicitly internal linkage. So you have to
write:
extern char const constMessage[] = "Const message";
(Another alternative would be for it to be a static class member.
Static class members always have external linkage.)
The case of string literals is in some ways similar: their type is
char const[]. But it's even worse: template instantiations (at least
the early ones) need a name, and a string literal doesn't have one.
Even more to the point, it's unspecified whether identical string literals
are the same object or not, so in the following:
template <char const* m>
struct Toto { char const* f() const; };
Toto <"titi"> t1;
Toto <"titi"> t2;
it would be unspecified whether t1 and t2 had the same type or not.
From the c++11 standard §14.3.2.1
Template non-type arguments
A template-argument for a non-type, non-template template-parameter shall be one of:
for a non-type template-parameter of integral or enumeration type,
a converted constant expression (5.19) of the type of the
template-parameter; or
the name of a non-type template-parameter; or
a constant expression (5.19) that designates the address of an
object with static storage duration and external or internal linkage
or a function with external or internal linkage, including function
templates and function template-ids but excluding non-static class
members, expressed (ignoring parentheses) as & id-expression, except
that the & may be omitted if the name refers to a function or array
and shall be omitted if the corresponding template-parameter is a
reference; or
a constant expression that evaluates to a null pointer value (4.10); or
a constant expression that evaluates to a null member pointer value (4.11); or
a pointer to member expressed as described in 5.3.1; or
an address constant expression of type std::nullptr_t.
To your questions:
Why the namespace_const_message and const_message aren't available at compile-time and thus forbidden in the print template function?
That's why constexpr exists. They can be used where it's needed compile-time evaluation, thus available to be template-arguments.
Is my guess about the string literals correct?
There is a note about this right after the arguments:
Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not
an acceptable template-argument.
You can also use a std::array<char, N>. The sample below requires C++20:
#include <array> // std::array
#include <cstddef> // std::size_t
template <auto constexpr_string>
void needs_constexpr_string() {
// ... use the string ...
}
template <auto N>
consteval auto str(char const (&cstr)[N]) {
std::array<char, N> arr;
for (std::size_t i = 0; i < N; ++i)
arr[i] = cstr[i];
return arr;
}
int main() {
needs_constexpr_string<str("Hello World")>();
}
I am trying to avoid adding an explicit specialization for my class template for the case that the length of an array becomes 0. It turns out that std::swap() cannot handle it:
#include <algorithm>
int main() {
int a[0], b[0];
std::swap(a, b); // g++-4.8 compile error
}
I thought that there should be some SFINAE in place to prevent such an error, no? Clearly, doing nothing is the right thing in this case.
If the standard forces std::swap() to raise a compiler error, can I manually add a compile-time if that checks if a non-type template parameter std::size_t N is 0?
Edit
Indeed, std::array<T, 0> is a specialized template that avoids declaring a zero-size array. From gcc-4.8.2/libstdc++-v3/include/std/array:
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
};
template<typename _Tp>
struct __array_traits<_Tp, 0>
{
struct _Type { };
static constexpr _Tp&
_S_ref(const _Type&, std::size_t) noexcept
{ return *static_cast<_Tp*>(nullptr); }
};
This is undefined behavior in C++ to define zero sized array.
C++ Standard n3337 § 8.3.4/1
If the constant-expression (5.19) is present, it shall be an integral
constant expression and its value shall be greater than zero.
It is valid however to create zero-sized array dynamically with array new: new[]:
C++ Standard n3337 § 5.3.4/6
Every constant-expression in a noptr-new-declarator shall be an
integral constant expression (5.19) and evaluate to a strictly
positive value. The expression in a noptr-new-declarator shall be of
integral type, unscoped enumeration type, or a class type for which a
single non-explicit conversion function to integral or unscoped
enumeration type exists (12.3). If the expression is of class type,
the expression is converted by calling that conversion function, and
the result of the conversion is used in place of the original
expression.
C++ Standard n3337 § 5.3.4/7
When the value of the expression in a noptr-new-declarator is zero,
the allocation function is called to allocate an array with no
elements. If the value of that expression is less than zero or such
that the size of the allocated object would exceed the
implementation-defined limit, or if the new-initializer is a braced-
init-list for which the number of initializer-clauses exceeds the
number of elements to initialize, no storage is obtained and the
new-expression terminates by throwing an exception of a type that
would match a handler (15.3) of type std::bad_array_new_length
(18.6.2.2).
Disclaimer: It's already been explained that C++ doesn't allow zero-length arrays, so creating/swapping them is undefined behavior. Zero-length arrays are supported by gcc as an extension. So everything that follows may only apply to gcc, and no other compiler.
The compilation error says nothing about arrays of zero-length. There are warnings about them if you enable -pedantic but they're not rejected outright. Instead the compiler is complaining about an invalid assignment. The reason for that is quite interesting.
std::swap has an overload for array types. However, because a zero-length array is not considered a valid array type, this overload is not the one selected when you pass in a zero-length array. This can be demonstrated with the following code:
template<typename T, std::size_t N>
void foo(T const (&)[N])
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
template<typename T>
void foo(T const&)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
Pass a non-zero length array to foo and the output is
void foo(const T (&)[N]) [with T = int; long unsigned int N = 10ul]
Now pass a zero-length array to foo and the output changes
void foo(const T&) [with T = int [0]]
Live demo
Now, back to the reason for the error. The std::swap implementation for non-arrays will move/copy one argument to a local variable, then the second argument to the first, and finally the local variable to the second argument. It is this series of move/copy initialization and assignments that goes wrong.
T temp = move(arg1);
arg2 = move(arg2);
arg1 = move(temp);
None of the above statements are valid when T=int[0], hence the error.
The easiest way to work around this problem is to use std::array. It has special support for zero-length arrays and swapping them will work correctly.
Otherwise, if you want to continue to rely on the non-portable gcc extension, I'd make a wrapper for swap that has an overload accepting zero-length arrays. The wrapper would call std::swap in all other cases.
template<typename T>
void my_swap(T& arg1, T& arg2)
{
using std::swap;
swap(arg1, arg2);
}
template<typename T, std::size_t N>
void my_swap(T (&arg1)[N], T (&arg2)[N])
{
using std::swap;
swap(arg1, arg2);
}
template<typename T>
void my_swap(T (&)[0], T (&)[0])
{
// do nothing
}
Live demo
void foo(const char *s);
is equivalent to:
void foo(const char s[]);
Are there similar equivalents to the following two?
void foo(char * const s);
void foo(const char * const s);
In C++, the compiler will automatically convert function parameters of type array of N elements of type T (where N can be unknown) into pointer to T. In the same transformation top level const qualifiers for the arguments are dropped:
void f( const int x ); // => declares: void f( int )
void f( int * const ); // => declares: void f( int* )
That means that in the pointer case, the top level const qualifier is removed, and in the case of the array it is converted to pointer to. Now, on the other hand you cannot mix both, only because you cannot declare a constant array of N elements of type T, since arrays are always const. That means that you cannot write:
void f( const int a[] const ); // Invalid type definition
As the type of the parameter is invalid. If it was a valid type, then the conversions would apply, but because it is not, the compiler will reject the code before trying to perform the conversion.
This is treated in §8.3.5/3 of the C++03 standard (and probably somewhere close in C++11)
A single name can be used for several different functions in a single scope; this is function overloading (clause 13). All declarations for a function with a given parameter list shall agree exactly both in the type of the value returned and in the number and type of parameters; the presence or absence of the ellipsis is considered part of the function type. The type of a function is determined using the following rules. The type of each parameter is determined from its own decl-specifier-seq and declarator. After determining the type of each parameter, any parameter of type “array of T” or “function returning T” is adjusted to be “pointer to T” or “pointer to function returning T,” respectively. After producing the list of parameter types, several transformations take place upon these types to determine the function type. Any cv-qualifier modifying a parameter type is deleted. [Example: the type void(*)(const int) becomes void(*)(int) —end example] Such cv-qualifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. If a storage-class-specifier modifies a parameter type, the specifier is deleted. [Example: register char* becomes char* —end example] Such storage-class-specifiers affect only the definition of the parameter within the body of the function; they do not affect the function type. The resulting list of transformed parameter types is the function’s parameter type list.
Note that since the compiler will perform that conversion, it is better to write the actual type that is going to be used by the compiler, following the principle of least surprise:
void f( int a[10] ) { a[5] = 7; }
The compiler is not going to check that the passed array has 10 elements, it reads the declaration as void f( int * ), and will gladly accept a call with an array of less elements or even no array at all (a pointer to a single int). Using a pointer in the actual code:
void f( int *a ) { a[5] = 7; }
Will likely trigger some alarms in a code review: are we guaranteed that in all calls to f the argument will be at least 6 elements big? Should we not pass also the size just in case?
You cannot in C89, but in C99 you can declare the equivalents as:
void foo(char s[const]);
void foo(const char s[const]);
this will be useful in some cases:
class AA {
void foo(char a[]);
void foo(const char a[]);
};
void AA::foo(char* const a) { … }
void AA::foo(const char* const a) { … }
and in C:
extern void foo(char a[]);
extern void fooc(const char a[]);
void foo(char* const a) { … }
void fooc(const char* const a) { … }
I thought that a pointer can be null, while an array argument cannot be null (and that the compiler is permitted to optimize knowing that; however on a simple example gcc-4.6 don't do such an optimization, even with -O3).
I am expecting that the compiler would optimize differently the two functions below. It does not. I don't have my C standard at hand to check if it could remove the test in ss below.
int s (int *t)
{
if (!t)
return 0;
return t[0] + t[1];
}
int ss (int t[])
{
if (!t) // never false, since t is an array!!
return 0;
return t[0] + t[1];
}