- 関数から(プレーンな)配列を返すことはできません。
const char[n]
constexpr(§7.1.5/ 3dcl.constexpr)内に新しいものを作成することはできません。
- アドレス定数式は、静的ストレージ期間のオブジェクトを参照する必要があります(§5.19/ 3expr.const)-これにより、constexpr ctorが配列を連結用にアセンブルし、constexprfctがそれをptrに変換するタイプのオブジェクトでのトリックを禁止します。 。
- constexprに渡される引数はコンパイル時の定数とは見なされないため、実行時にfctを使用することもできます。これにより、テンプレートメタプログラミングでのいくつかのトリックが許可されなくなります。
- テンプレート引数として関数に渡される文字列リテラルの単一の文字を取得することはできません。これにより、他のテンプレートメタプログラミングのトリックが許可されなくなります。
したがって(私が知る限り)、char const*
新しく構築された文字列またはを返すconstexprを取得することはできませんchar const[n]
。std::array
Xeoが指摘しているように、これらの制限のほとんどは当てはまらないことに注意してください。
また、いくつかを返すことができたとしてもchar const*
、戻り値はリテラルではなく、隣接する文字列リテラルのみが連結されます。これは、翻訳フェーズ6(§2.2)で発生します。これは、私がまだ前処理フェーズと呼んでいます。Constexprは後で評価されます(参照?)。(f(x) f(y)
ここf
で、関数は構文エラーafaikです)
ただし、constexpr fctから、両方の文字列を含み、に挿入/印刷できる他のタイプのオブジェクト(constexpr ctorまたは集合体を使用)を返すことができますbasic_ostream
。
編集:これが例です。かなり長いですoO文字列の末尾に「\n」を追加するためだけに、これを合理化できることに注意してください。(これは、私がメモリから書き留めたばかりのより一般的なアプローチです。)
Edit2:実際、あなたはそれを本当に合理化することはできません。arr
(文字列リテラルの配列ではなく)「\ n」が含まれる「constchar_typeの配列」としてデータメンバーを作成すると、実際には少し長い可変個引数テンプレートコードが使用されます(ただし、機能します。Xeoの回答を参照してください)。
注:(ct_string_vector
名前は適切ではありません)ポインターを格納するため、静的ストレージ期間の文字列(リテラルやグローバル変数など)でのみ使用する必要があります。利点は、テンプレートメカニズムによって文字列をコピーおよび展開する必要がないことです。constexprを使用して結果を保存する場合(例のようにmain
)、渡されたパラメーターが静的保存期間でない場合、コンパイラーは文句を言う必要があります。
#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;
}