C++ 字符串文字引用类

C++ 字符串文字引用类,c++,c++14,C++,C++14,在C++1y中,我可以拥有一个绑定到字符串文本但不绑定到char*或char[]&或类似对象的引用类吗 class LitRf{ const char* data_; size_t size_; public: LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ } }; C++11删除了检测字符串文字的唯一形式化方法,即通过隐式转换为指向非常量的指针 无论如何,使用这个小把戏,我们必须使用一个宏 在C++1

C++1y
中,我可以拥有一个绑定到字符串文本但不绑定到
char*
char[]&
或类似对象的引用类吗

class LitRf{
  const char* data_;
  size_t size_;
public:
  LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ }
};

C++11删除了检测字符串文字的唯一形式化方法,即通过隐式转换为指向非常量的指针

无论如何,使用这个小把戏,我们必须使用一个宏

在C++11及更高版本中,最好的方法是建立一个强大的约定,即
const
字符类型的数组是一个文本

也就是说,以你的例子

class LitRf
{
private:
    const char* data_;
    Sz size_;

    template< size_t n >
    LitRf( char (&)[n] ) = delete;

public:
    template< size_t n >
    LitRf( char const (&s)[n] )
        : data_{s}, size_{sizeof(s) - 1}
    {}
};
class-LitRf
{
私人:
常量字符*数据;
Sz尺寸;
模板
LitRf(char(&)[n])=删除;
公众:
模板
LitRf(字符常量(&s)[n])
:data{s},size{sizeof(s)-1}
{}
};

注意使用
size\u t
而不是可能的有符号类型
Sz
。这确保代码将使用g++编译。不幸的是,该编译器或其旧版本存在一个bug,它坚持使用
size\t
,或者拒绝接受该代码。

我想您最好使用
const char(&s)[N]
(带
模板
)作为参数类型。但它也绑定到字符串文字以外的任何常量字符数组

添加已删除的非常量字符数组构造函数,以禁止使用非常量数组调用它

class LitRf
{
    const char* data_;
    Sz size_;
public:
    template<size_t N>
    LitRf(char const (&s)[N])
        : data_{s}, size_{N}
    {}

    template<size_t N>
    LitRf(char (&s)[N]) = delete;
};
其思想是连接两个字符串文字,其中第二个只是一个空字符串。只有当第一个也是字符串文字时,才可能这样做;将变量放在此处会出现语法错误。宏扩展后,编译器会看到类似于
LitRf(“foo”)的内容
,这相当于
LitRf(“foo”)
。一些例子:

auto x = MakeLitRf("foo");  // works

const char *foo = "foo";
auto x = MakeLitRf(foo);    // fails

auto x = LitRf(foo);        // works, but we want it to fail...
在最后一种情况下,用户无意(或有意)没有使用宏,使我们的工作毫无价值。要使其也失败,请向构造函数添加一个隐藏参数,直接调用时需要添加该参数(当然,在宏的定义中也是如此):


我能想到的最好办法是提供一个由static_断言的模板重载,或者使用explicit关键字。@Borleader但我认为没有办法区分字符串文字和常量字符数组。@Borleader您如何
static_断言
某个东西是字符串文字?那么,
explicit
在这里有什么帮助呢?实际上,如果您可以
static\u assert
断言某个东西是字符串文字,那么您也可以使用与SFINAE相同的条件来控制重载解析,以便不首先绑定到函数。@Borgleader请共享它。我知道。你不能改变这一点。常数始终可以添加,但不能删除。您只能添加非常量重载,但
=删除它。谢谢
char const[]
~
string literal
是一个合理的折衷方案。不幸的是,这也将从非常量字符数组初始化。(别管
Sz
。我不知道它是什么——我用它作为
std::size_t
typedef
,在我的命名空间中)但是你不能区分
const char
数组和
const
char
数组的
Why
sizeof(s)-1
?我知道,终止0,但OP最初并没有减去它。啊,算了吧-我想OP不是有意做
sizeof(s)
因为在他的例子中这是1-他可能指的是
strlen
,而strlen确实会匹配您的代码。添加了一个不可访问且已删除的构造函数,以防止从非
常量的数组中构造。
#define MakeLitRf(s) LitRf(s "")
auto x = MakeLitRf("foo");  // works

const char *foo = "foo";
auto x = MakeLitRf(foo);    // fails

auto x = LitRf(foo);        // works, but we want it to fail...
class LitRf
{
    const char* data_;
    Sz size_;
public:
    // Called by the macro MakeLitRf. Unlikely to be called directly unless the user knows what he's doing.
    LitRf(const char *s, void *)
        : data_{s}, size_{N}
    {}

    // Called without macro! Throw a compiler error, explaining what's wrong.
    LitRf(const char *s)
    {
        static_assert(false, "Please use the macro `MakeLitRf` with a string literal to construct a `LitRf`.");
    }
};

#define MakeLitRf(s) LitRf(s "", nullptr)