将传递的参数限制为字符串文字

发布于 2024-12-07 04:43:03 字数 438 浏览 0 评论 0原文

我有一个类来包装字符串文字并在编译时计算大小。

构造函数如下所示:

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 字符串文字?编译时检测是首选,但如果没有更好的方法,运行时检测也是可以接受的。

I have a class to wrap string literals and calculate the size at compile time.

The constructor looks like this:

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() );

There is problem with the code however. The following code compiles and I would like to make it an error.

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

Is there a way to restrict the constructor so that it only accepts c string literals? Compile time detection is preferred, but runtime is acceptable if there is no better way.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

唔猫 2024-12-14 04:43:03

有一种方法可以强制使用字符串文字参数:创建用户定义的文字运算符。您可以使运算符 constexpr 在编译时获取大小:

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

我不知道目前有任何编译器实现此功能。

There is a way to force a string literal argument: make a user defined literal operator. You can make the operator constexpr to get the size at compile time:

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

I don't know of any compiler that implements this feature at this time.

能否归途做我良人 2024-12-14 04:43:03

。您可以使用以下预处理器生成编译时错误

#define IS_STRING_LITERAL(X) "" X ""

如果您尝试传递字符串文字以外的任何内容,则编译将失败。用法:

Literal greet(IS_STRING_LITERAL("Hello World!"));  // ok
Literal greet(IS_STRING_LITERAL(broke)); // error

Yes. You can generate compile time error with following preprocessor:

#define IS_STRING_LITERAL(X) "" X ""

If you try to pass anything other than a string literal, the compilation will fail. Usage:

Literal greet(IS_STRING_LITERAL("Hello World!"));  // ok
Literal greet(IS_STRING_LITERAL(broke)); // error
祁梦 2024-12-14 04:43:03

借助完全支持 constexpr 的 C++11 编译器,我们可以使用 constexpr 函数来使用 constexpr 构造函数,该函数会编译为非const 表达式主体,以防尾随零字符前提条件未满足,导致编译失败并出现错误。以下代码扩展了 UncleBens 的代码,灵感来自 Andrzej 的 C++ 博客的一篇文章

#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

我用 gcc 4.8.2 测试了这段代码。使用 MS Visual C++ 2013 CTP 编译失败,因为它仍然不完全支持 constexpr(仍然不支持 constexpr 成员函数)。

也许我应该提到,我的第一个(也是首选)方法是简单地插入

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

构造函数主体中。它因编译错误而失败,并且 constexpr 构造函数似乎必须有一个空主体。我不知道这是否是 C++11 的限制以及未来标准是否会放宽。

With a C++11 compiler with full support for constexpr we can use a constexpr constructor using a constexpr function, which compiles to a non-const expression body in case the trailing zero character precondition is not fulfilled, causing the compilation to fail with an error. The following code expands the code of UncleBens and is inspired by an article of Andrzej's C++ blog:

#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

I tested this code with gcc 4.8.2. Compilation with MS Visual C++ 2013 CTP failed, as it still does not fully support constexpr (constexpr member functions still not supported).

Probably I should mention, that my first (and preferred) approach was to simply insert

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

in the constructor body. It failed with a compilation error and it seems, that constexpr constructors must have an empty body. I don't know, if this is a C++11 restriction and if it might be relaxed by future standards.

冷情妓 2024-12-14 04:43:03

不,没有办法做到这一点。字符串文字具有特定类型,并且所有方法重载解析都是在该类型上完成的,而不是它是字符串文字。任何接受字符串文字的方法最终都会接受任何具有相同类型的值。

如果您的函数绝对依赖于字符串文字来运行,那么您可能需要重新访问该函数。这取决于它无法保证的数据。

No there is no way to do this. String literals have a particular type and all method overload resolution is done on that type, not that it's a string literal. Any method which accepts a string literal will end up accepting any value which has the same type.

If your function absolutely depends on an item being a string literal to function then you probably need to revisit the function. It's depending on data it can't guarantee.

深爱不及久伴 2024-12-14 04:43:03

字符串文字没有单独的类型来区分它与 const 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
}

至于运行时检查,非文字的唯一问题是它可能不是空终止的?当您知道数组的大小时,您可以对其进行循环(最好向后)以查看其中是否有 \0

A string literal does not have a separate type to distinguish it from a const char array.

This, however, will make it slightly harder to accidentally pass (non-const) char arrays.

#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
}

As to runtime checking, the only problem with a non-literal is that it may not be null-terminated? As you know the size of the array, you can loop over it (preferable backwards) to see if there's a \0 in it.

我纯我任性 2024-12-14 04:43:03

我曾经提出过一个 C++98 版本,它使用的方法类似于 @k.st 提出的方法。为了完整起见,我将添加此内容以解决有关 C++98 宏的一些批评。
此版本试图通过防止通过私有构造函数直接构造并将唯一可访问的工厂函数移动到详细名称空间中来强制执行良好行为,而详细名称空间又由“官方”创建宏使用。不完全漂亮,但更安全一点。这样,如果用户想要行为不当,则至少必须显式使用明显标记为内部的功能。一如既往,没有办法防止故意恶意行为。

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, "")

I once came up with a C++98 version that uses an approach similar to the one proposed by @k.st. I'll add this for the sake of completeness to address some of the critique wrt the C++98 macro.
This version tries to enforce good behavior by preventing direct construction via a private ctor and moving the only accessible factory function into a detail namespace which in turn is used by the "offical" creation macro. Not exactly pretty, but a bit more fool proof. This way, users have to at least explicitly use functionality that is obviously marked as internal if they want to misbehave. As always, there is no way to protect against intentional malignity.

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, "")
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文