【问题标题】:String literal reference class [duplicate]字符串文字引用类[重复]
【发布时间】:2016-03-03 12:29:56
【问题描述】:

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

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

【问题讨论】:

  • 我能想到的最好办法是提供一个 static_asserts 的模板化重载,或者使用显式关键字。
  • @Borgleader 但我认为没有办法区分字符串文字和 const char 数组。
  • @Borgleader 你怎么会static_assert 那东西是字符串文字? explicit 怎么能在这里提供帮助?实际上,如果你可以static_assert 知道某事是字符串文字,你也可以使用与 SFINAE 相同的条件来控制重载决议,而不是一开始就绑定到函数。
  • @Borgleader 请分享。
  • 我知道。你无法改变这一点。始终可以添加常量,但不能删除。您只能添加一个非常量重载,但 =delete 它。

标签: c++ c++14


【解决方案1】:

我想你能做的最好的就是使用const char (&s)[N](和template<size_t N>)作为参数类型。但它也绑定到字符串文字以外的任何 const char 数组。

添加一个已删除的非常量char数组构造函数,禁止用非常量数组调用。

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

除此之外,您还可以使用宏包装器,它(当没有它时从不使用构造函数时)只能从字面量构造对象,甚至不能通过变量。

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

这个想法是连接两个字符串文字,其中第二个只是一个空字符串。这只有在第一个也是字符串文字时才有可能;放一个变量有语法错误。在宏展开之后,编译器会看到类似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...

在最后一种情况下,用户无意(或有意?)没有使用宏,使我们的工作毫无价值。为了让它也失败,给构造函数添加一个隐藏参数,需要在直接调用时添加(当然是在宏的定义中):

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)

【讨论】:

    【解决方案2】:

    C++11 删除了检测字符串字面量的唯一正式方法,即通过隐式转换为指向非 const 的指针。

    无论如何使用这个小技巧都必须使用宏。

    在 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}
        {}
    };
    

    注意使用size_t 而不是可能有符号的类型Sz。这确保代码将使用 g++ 编译。不幸的是,那个编译器,或者它的旧版本,有一个错误,它非常坚持size_t,否则它拒绝接受代码。

    【讨论】:

    • 谢谢。 char const [] ~ string literal 是一个合理的妥协。不幸的是,这也将从非常量字符数组初始化。 (别介意Sz。我不知道它是什么——我在我的命名空间中将它用作std::size_ttypedef
    • 但是您无法区分const char 的数组和charconst 数组
    • 为什么是sizeof(s) - 1?我知道,终止 0,但 OP 最初并没有减去它。
    • 啊,算了 - 我猜 OP 并不是要这样做 sizeof(s) 因为在他的情况下这是 1 - 他可能是指 strlen 这反过来确实会匹配您的代码。
    • 添加了一个无法访问和删除的构造函数,以防止从非constchar的数组构造。
    【解决方案3】:
    class LitRf{
      const char* data_;
      Sz size_;
    public:
      LitRf(const char* /*?*/ s) : data_{s},size_{sizeof(s)} { /*?*/ }
      LitRf(char*) = delete;
    };
    

    【讨论】:

      猜你喜欢
      • 2015-02-13
      • 1970-01-01
      • 2011-06-08
      • 2019-04-10
      • 2010-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-03
      相关资源
      最近更新 更多