10

文字列リテラルをラップし、コンパイル時にサイズを計算するクラスがあります。

コンストラクターは次のようになります。

template< std::size_t N >
Literal( const char (&literal)[N] );

// used like this
Literal greet( "Hello World!" );
printf( "%s, length: %d", greet.c_str(), greet.size() );

ただし、コードに問題があります。次のコードがコンパイルされ、エラーになります。

char broke[] = { 'a', 'b', 'c' };
Literal l( broke );

c文字列リテラルのみを受け入れるようにコンストラクターを制限する方法はありますか?コンパイル時の検出が推奨されますが、より良い方法がない場合はランタイムを使用できます。

4

6 に答える 6

11

文字列リテラル引数を強制する方法があります。ユーザー定義のリテラル演算子を作成します。演算子constexprを作成して、コンパイル時にサイズを取得できます。

constexpr Literal operator "" _suffix(char const* str, size_t len) {
    return Literal(chars, len);
}

現時点では、この機能を実装しているコンパイラはありません。

于 2011-09-30T17:08:43.650 に答える
6

はい。次のプリプロセッサを使用すると、コンパイル時エラーが発生する可能性があります。

#define IS_STRING_LITERAL(X) "" X ""

文字列リテラル以外のものを渡そうとすると、コンパイルは失敗します。使用法:

Literal greet(IS_STRING_LITERAL("Hello World!"));  // ok
Literal greet(IS_STRING_LITERAL(broke)); // error
于 2011-09-30T17:47:00.093 に答える
3

完全にサポートされているC++11コンパイラでは、関数を使用してコンストラクタを使用constexprできます。このコンストラクタは、末尾のゼロ文字の前提条件が満たされない場合に非定数式の本体にコンパイルされ、コンパイルがエラーで失敗します。次のコードは、UncleBensのコードを拡張したもので、AndrzejのC++ブログの記事に触発されています。constexprconstexpr

#include <cstdlib>

class Literal
{
  public:

    template <std::size_t N> constexpr
    Literal(const char (&str)[N])
    : mStr(str),
      mLength(checkForTrailingZeroAndGetLength(str[N - 1], N))
    {
    }

    template <std::size_t N> Literal(char (&str)[N]) = delete;

  private:
    const char* mStr;
    std::size_t mLength;

    struct Not_a_CString_Exception{};

    constexpr static
    std::size_t checkForTrailingZeroAndGetLength(char ch, std::size_t sz)
    {
      return (ch) ? throw Not_a_CString_Exception() : (sz - 1);
    }
};

constexpr char broke[] = { 'a', 'b', 'c' };

//constexpr Literal lit = (broke); // causes compile time error
constexpr Literal bla = "bla"; // constructed at compile time

このコードをgcc4.8.2でテストしました。MS Visual C ++ 2013 CTPを使用したコンパイルは、まだ完全にはサポートされていないため失敗しましたconstexprconstexprメンバー関数はまだサポートされていません)。

おそらく私が言及する必要があるのは、私の最初の(そして好ましい)アプローチは単に挿入することでした

static_assert(str[N - 1] == '\0', "Not a C string.")

コンストラクター本体で。コンパイルエラーで失敗し、constexprコンストラクターは空の本体を持っている必要があるようです。これがC++11の制限であるかどうか、また将来の標準によって緩和される可能性があるかどうかはわかりません。

于 2014-03-30T13:44:56.067 に答える
2

いいえ、これを行う方法はありません。文字列リテラルには特定のタイプがあり、すべてのメソッドのオーバーロード解決は、文字列リテラルではなく、そのタイプで行われます。文字列リテラルを受け入れるメソッドは、同じタイプの値を受け入れることになります。

関数が機能する文字列リテラルであるアイテムに完全に依存している場合は、おそらく関数を再検討する必要があります。保証できないデータによって異なります。

于 2011-09-30T16:50:21.477 に答える
0

文字列リテラルには、constchar配列と区別するための個別の型はありません。

ただし、これにより、誤って(非定数)char配列を渡すことが少し難しくなります。

#include <cstdlib>

struct Literal
{
    template< std::size_t N >
    Literal( const char (&literal)[N] ){}

    template< std::size_t N >
    Literal( char (&literal)[N] ) = delete;
};

int main()
{
    Literal greet( "Hello World!" );
    char a[] = "Hello world";
    Literal broke(a); //fails
}

ランタイムチェックに関して、非リテラルの唯一の問題は、nullで終了しない可能性があることです。配列のサイズがわかっているので、配列をループして(逆方向にできれば)、配列にが含まれているかどうかを確認できます\0

于 2011-09-30T18:37:08.517 に答える
0

私はかつて、@k.stによって提案されたものと同様のアプローチを使用するC++98バージョンを思いついた。完全を期すためにこれを追加して、C++98マクロに関する批評の一部に対処します。このバージョンは、プライベートctorを介した直接構築を防ぎ、アクセス可能な唯一のファクトリ関数を「公式」作成マクロによって使用される詳細名前空間に移動することにより、適切な動作を強制しようとします。正確にはきれいではありませんが、もう少しばかげた証拠です。このように、ユーザーは、誤動作したい場合に、明らかに内部としてマークされている機能を少なくとも明示的に使用する必要があります。いつものように、意図的な悪意から保護する方法はありません。

class StringLiteral
{
private:
    // Direct usage is forbidden. Use STRING_LITERAL() macro instead.
    friend StringLiteral detail::CreateStringLiteral(const char* str);
    explicit StringLiteral(const char* str) : m_string(str)
    {}

public:
    operator const char*() const { return m_string; }

private:
    const char* m_string;
};

namespace detail {

StringLiteral CreateStringLiteral(const char* str)
{
    return StringLiteral(str);
}

} // namespace detail

#define STRING_LITERAL_INTERNAL(a, b) detail::CreateStringLiteral(a##b)

/**
*   \brief The only way to create a \ref StringLiteral "StringLiteral" object.
*   This will not compile if used with anything that is not a string literal.
*/
#define STRING_LITERAL(str) STRING_LITERAL_INTERNAL(str, "")
于 2017-08-16T11:50:43.577 に答える