将传递的参数限制为字符串字面值
Restrict passed parameter to a string literal
我有一个类来包装字符串文字并在编译时计算大小。
构造函数如下:
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字符串字面量?编译时检测是首选,但如果没有更好的方法,运行时检测也是可以接受的。
有一种方法可以强制使用字符串文字参数:创建一个用户定义的文字操作符。您可以使用操作符constexpr
在编译时获取大小:
constexpr Literal operator "" _suffix(char const* str, size_t len) {
return Literal(chars, len);
}
目前我还不知道有哪个编译器实现了这个特性
是。使用以下预处理器可以生成编译时错误:
#define IS_STRING_LITERAL(X) "" X ""
如果您试图传递字符串字面值以外的任何内容,则编译将失败。用法:
Literal greet(IS_STRING_LITERAL("Hello World!")); // ok
Literal greet(IS_STRING_LITERAL(broke)); // error
对于完全支持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] == ' ', "Not a C string.")
构造函数体中的。它失败了一个编译错误,看起来,constexpr
构造函数必须有一个空的体。我不知道这是否是c++ 11的限制,也不知道未来的标准是否会放宽。
没有办法做到这一点。字符串字面值有一个特定的类型,所有的方法重载解析都是在这个类型上完成的,而不是它是一个字符串字面值。任何接受字符串字面值的方法最终都会接受具有相同类型的任何值。
如果你的函数完全依赖于一个字符串字面值的项来运行,那么你可能需要重新访问这个函数。这取决于它无法保证的数据。
字符串字面值没有单独的类型来区分它与const char数组。
然而,这会使意外传递(非const)字符数组变得稍微困难一些。
#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
}
至于运行时检查,非文字的唯一问题是它可能不是以空结束的?由于您知道数组的大小,您可以遍历它(最好是向后)以查看其中是否存在