- You cannot return a (plain) array from a function.
- You cannot create a new
const char[n]
inside a constexpr (§7.1.5/3 dcl.constexpr).
- An address constant expression must refer to an object of static storage duration (§5.19/3 expr.const) - this disallows some tricks with objects of types having a constexpr ctor assembling the array for concatenation and your constexpr fct just converting it to a ptr.
- The arguments passed to a constexpr are not considered to be compile-time constants so you can use the fct at runtime, too - this disallows some tricks with template metaprogramming.
- You cannot get the single char's of a string literal passed to a function as template arguments - this disallows some other template metaprogramming tricks.
So (as far as I know), you cannot get a constexpr that is returning a char const*
of a newly constructed string or a char const[n]
. Note most of these restrictions don't hold for an std::array
as pointed out by Xeo.
And even if you could return some char const*
, a return value is not a literal, and only adjacent string literals are concatenated. This happens in translation phase 6 (§2.2), which I would still call a preprocessing phase. Constexpr are evaluated later (ref?). (f(x) f(y)
where f
is a function is a syntax error afaik)
But you can return from your constexpr fct an object of some other type (with a constexpr ctor or that is an aggregate) that contains both strings and can be inserted/printed into an basic_ostream
.
Edit: here's the example. It's quite a bit long o.O
Note you can streamline this in order just to get an additional "\n" add the end of a string. (This is more a generic approach I just wrote down from memory.)
Edit2: Actually, you cannot really streamline it. Creating the arr
data member as an "array of const char_type" with the '\n' included (instead of an array of string literals) uses some fancy variadic template code that's actually a bit longer (but it works, see Xeo's answer).
Note: as ct_string_vector
(the name's not good) stores pointers, it should be used only with strings of static storage duration (such as literals or global variables). The advantage is that a string does not have to be copied & expanded by template mechanisms. If you use a constexpr to store the result (like in the example main
), you compiler should complain if the passed parameters are not of static storage duration.
#include <cstddef>
#include <iostream>
#include <iterator>
template < typename T_Char, std::size_t t_len >
struct ct_string_vector
{
using char_type = T_Char;
using stringl_type = char_type const*;
private:
stringl_type arr[t_len];
public:
template < typename... TP >
constexpr ct_string_vector(TP... pp)
: arr{pp...}
{}
constexpr std::size_t length()
{ return t_len; }
template < typename T_Traits >
friend
std::basic_ostream < char_type, T_Traits >&
operator <<(std::basic_ostream < char_type, T_Traits >& o,
ct_string_vector const& p)
{
std::copy( std::begin(p.arr), std::end(p.arr),
std::ostream_iterator<stringl_type>(o) );
return o;
}
};
template < typename T_String >
using get_char_type =
typename std::remove_const <
typename std::remove_pointer <
typename std::remove_reference <
typename std::remove_extent <
T_String
> :: type > :: type > :: type > :: type;
template < typename T_String, typename... TP >
constexpr
ct_string_vector < get_char_type<T_String>, 1+sizeof...(TP) >
make_ct_string_vector( T_String p, TP... pp )
{
// can add an "\n" at the end of the {...}
// but then have to change to 2+sizeof above
return {p, pp...};
}
// better version of adding an '\n':
template < typename T_String, typename... TP >
constexpr auto
add_newline( T_String p, TP... pp )
-> decltype( make_ct_string_vector(p, pp..., "\n") )
{
return make_ct_string_vector(p, pp..., "\n");
}
int main()
{
// ??? (still confused about requirements of constant init, sry)
static constexpr auto assembled = make_ct_string_vector("hello ", "world");
enum{ dummy = assembled.length() }; // enforce compile-time evaluation
std::cout << assembled << std::endl;
std::cout << add_newline("first line") << "second line" << std::endl;
}
"usage: foo\n" "print a message\n"
? – Lorrinelorrystd::endl
rather than\n
– Peta"usage: foo\nprint a message\n"
? – Guianastd::endl
is overused when you just want'\n'
. So I don't thinkstd::endl
should be used in place of'\n'
. – Cogswellostream
. – Suttle'\n'
. Butstd::endl
is the default. (In my own code, if I'm outputting several lines with no intervening operations, I'll use'\n'
on all but the last. But I would consider this as an "advanced technique". Beginners should usestd::endl
, period, until they understand the issues well enough to make a knowledgeable choice.) – Guianastd::endl
in favor of\n
defeats the whole point about buffered streams, its similar to introducing namespaces but then invokingusing namespace std;
. People should rely on streams doing the right (tm) thing and should not flush them, unless they now why the want to flush. – Uralaltaic\n
andstd::endl
as most appropriate. People who don't understand buffering (and it's generally not the first thing you explain when teaching C++) should usestd::endl
by default, on the principle of least surprise. – Guiana