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")>();
}
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;
I am playing around with some toy code using c++11 to figure out a bit more about how things work. During this I came across the following issue that simplifies down to:
template <int x, int y>
class add {
public:
static constexpr int ret = x + y;
};
constexpr int addFunc(const int x, const int y) {
return add<x,y>::ret;
}
int main() {
const int x = 1;
const int y = 2;
cout << add<x,y>::ret << endl; // Works
cout << addFunc(1,2) << endl; // Compiler error
return 0;
}
I'm using GCC 4.8.1 and the output is:
'x' is not a constant expression in template argument for type 'int'
'y' is not a constant expression in template argument for type 'int'
What exactly is the difference between the two ways I am trying to calculate add::ret? Both of these values should be available at compile time.
You tell the compiler, that addFunc would be a constexpr. But it depents on parameters, that are not constexpr itself, so the compiler already chokes on that. Marking them const only means you are not going to modify them in the function body, and the specific calls you make to the function are not considered at this point.
There is a way you can make the compiler understand you are only going to pass compile time constants to addFunc: Make the parameters a template parameters itself:
template <int x, int y>
constexpr int addFunc() {
return add<x,y>::ret;
}
Then call as
cout << addFunc<1,2>() << endl;
If your purpose is just to shorten code a bit, in C++14 you can create variable template:
template <int x, int y>
constexpr int addVar = x + y;
cout << addVar<5, 6> << endl; // Works with clang 3.5, fails on GCC 4.9.1
GCC 5 will also support this.
The compiler does not know if x and y are always available at compile time as constant values (expression), and what more, C++11/14 does not support constexpr function parameter, so there's no way x and y can be used as parameter for the template add<> in addFunc.
Function parameters of a constexpr function aren't constant expressions. The function is constexpr to the outside (as calling it might result in a constant expression), but calculations inside are just as constexpr as they would be in a normal function.
Template-arguments require constant expressions. These are the crucial requirements for constant expressions that aren't met in your code and thus produce the compiler error ([expr.const]/2, emphasis mine):
A conditional-expression is a core constant expression unless it
involves one of the following as a potentially evaluated subexpression
(3.2) […]:
— an lvalue-to-rvalue conversion (4.1) unless it is applied to
a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized
with a constant expression, or
a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an
object, or
a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant
expression;
You are applying an lvalue-to-rvalue conversion on the parameters to pass them as template arguments.
The first bullet item doesn't apply as the function parameter is neither precedingly initialized nor known to be initialized with a constant expression, and the second and third don't either (in particular, function parameters shall not be declared constexpr).
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
My compiler behaves oddly when I try to pass a fixed-size array to a template function. The code looks as follows:
#include <algorithm>
#include <iostream>
#include <iterator>
template <typename TSize, TSize N>
void f(TSize (& array)[N]) {
std::copy(array, array + N, std::ostream_iterator<TSize>(std::cout, " "));
std::cout << std::endl;
}
int main() {
int x[] = { 1, 2, 3, 4, 5 };
unsigned int y[] = { 1, 2, 3, 4, 5 };
f(x);
f(y); //line 15 (see the error message)
}
It produces the following compile error in GCC 4.1.2:
test.cpp|15| error: size of array has non-integral type ‘TSize’
test.cpp|15| error: invalid initialization of reference of type
‘unsigned int (&)[1]’ from expression of type ‘unsigned int [5]’
test.cpp|6| error: in passing argument 1 of ‘void f(TSize (&)[N])
[with TSize = unsigned int, TSize N = ((TSize)5)]’
Note that the first call compiles and succeeds. This seems to imply that while int is integral, unsigned int isn't.
However, if I change the declaration of my above function template to
template <typename TSize, unsigned int N>
void f(TSize (& array)[N])
the problem just goes away! Notice that the only change here is from TSize N to unsigned int N.
Section [dcl.type.simple] in the final draft ISO/IEC FDIS 14882:1998 seems to imply that an "integral type" is either signed or unsigned:
The signed specifier forces char objects and bit-fields to be signed; it is redundant with other integral types.
Regarding fixed-size array declarations, the draft says [dcl.array]:
If the constant-expression (expr.const) is present, it shall be an integral constant expression and its value shall be greater than zero.
So why does my code work with an explicit unsigned size type, with an inferred signed size type but not with an inferred unsigned size type?
EDIT Serge wants to know where I'd need the first version. First, this code example is obviously simplified. My real code is a bit more elaborate. The array is actually an array of indices/offsets in another array. So, logically, the type of the array should be the same as its size type for maximum correctness. Otherwise, I might get a type mismatch (e.g. between unsigned int and std::size_t). Admittedly, this shouldn't be a problem in practice since the compiler implicitly converts to the larger of the two types.
EDIT 2 I stand corrected (thanks, litb): size and offset are of course logically different types, and offsets into C arrays in particular are of type std::ptrdiff_t.
Hmm, the Standard says in 14.8.2.4 / 15:
If, in the declaration of a function template with a non-type template-parameter, the non-type template-parameter is used in an expression in the function parameter-list and, if the corresponding template-argument is deduced, the template-argument type shall match the type of the template-parameter exactly, except that a template-argument deduced from an array bound may be of any integral type.
Providing this example:
template<int i> class A { /* ... */ };
template<short s> void f(A<s>);
void k1() {
A<1> a;
f(a); // error: deduction fails for conversion from int to short
f<1>(a); // OK
}
That suggests that the compilers that fail to compile your code (apparently GCC and Digital Mars) do it wrong. I tested the code with Comeau, and it compiles your code fine. I don't think there is a different to whether the type of the non-type template parameter depends on the type of the type-parameter or not. 14.8.2.4/2 says the template arguments should be deduced independent from each other, and then combined into the type of the function-parameter. Combined with /15, which allows the type of the dimension to be of different integral type, i think your code is all fine. As always, i take the c++-is-complicated-so-i-may-be-wrong card :)
Update: I've looked into the passage in GCC where it spits out that error message:
...
type = TREE_TYPE (size);
/* The array bound must be an integer type. */
if (!dependent_type_p (type) && !INTEGRAL_TYPE_P (type))
{
if (name)
error ("size of array %qD has non-integral type %qT", name, type);
else
error ("size of array has non-integral type %qT", type);
size = integer_one_node;
type = TREE_TYPE (size);
}
...
It seems to have missed to mark the type of the size as dependent in an earlier code block. As that type is a template parameter, it is a dependent type (see 14.6.2.1).
Update: GCC developers fixed it: Bug #38950