C++:构造函数仅接受字符串文字

问题描述 投票:0回答:7

是否可以创建一个构造函数(或函数签名,就此而言),该构造函数仅接受字符串文字,但不接受例如char const *


是否可以有两个重载来区分字符串文字和

char const *


C++ 0x 会允许使用自定义后缀 - 但我正在寻找“更早的”解决方案。

基本原理:

避免以字符串文字形式给出时不会被修改的字符串的堆副本。 这些字符串直接进入需要

const char *

的 API,无需任何处理。大多数调用确实使用不需要额外处理的文字,只有在少数情况下才会构造它们。我正在寻找保留本机调用行为的可能性。


注意:

- 因为它出现在答案中:有问题的代码根本不使用 std::string,但一个很好的例子是:


class foo { std::string m_str; char const * m_cstr; public: foo(<string literal> s) : m_cstr(p) {} foo(char const * s) : m_str(s) { m_cstr = s.c_str(); } foo(std::string const & s) : m_str(s) { m_cstr = s.c_str(); } operator char const *() const { return m_cstr; } }



结果:

(1) 做不到。

(2) 我意识到我什至不是在寻找文字,而是在寻找编译时常量(即“不需要复制的任何内容”)。


我可能会使用以下模式:

const literal str_Ophelia = "Ophelia"; void Foo() { Hamlet(str_Ophelia, ...); // can receive literal or string or const char * }

用一个简单的

struct literal { char const * data; literal(char const * p) : data(p) {} operator const char *() const { return data; } };

这并不能阻止任何人滥用它(我应该找到一个更好的名字......),但它允许所需的优化,但默认情况下保持安全。

c++ constructor overloading string-literals
7个回答
21
投票
sbi 想法

的工作解决方案: struct char_wrapper { char_wrapper(const char* val) : val(val) {}; const char* val; }; class MyClass { public: template< std::size_t N > explicit MyClass(const char (&str)[N]) { cout << "LITERAL" << endl; } template< std::size_t N > explicit MyClass(char (&str)[N]) { cout << "pointer" << endl; } MyClass(char_wrapper m) { cout << "pointer" << endl; } }; int main() { MyClass z("TEST1"); // LITERAL const char* b = "fff"; MyClass a(b); // pointer char tmp[256]; strcpy(tmp, "hello"); MyClass c(tmp); // pointer }



20
投票
可以

完成!我想出了一个适用于 C++03 且没有包装类的解决方案(这会破坏 return 语句中的一些隐式转换)。 首先,您需要类型

const char (&)[N]

的构造函数模板,因为这是字符串文字的原始类型。然后,您还需要另一个类型

char (&)[N]
- 例如 char 缓冲区 - 这样它们就不会出现在文字的构造函数中。也许您还需要类型
const char*
的普通构造函数。

template<int N> Foo(const char (&)[N]); // for string literals template<int N> Foo(char (&)[N]); // for non-const char arrays like buffers Foo(const char*); // normal c strings

现在的问题是,对于字符串文字,编译器仍然认为
const char*

构造函数是比模板实例更好的选择,因为

数组到指针的转换
具有精确匹配等级。 (13.3.3.1.1) 因此,技巧是降低

const char*

构造函数的优先级。这也可以通过将其更改为模板并使用 SFINAE 仅将其与类型

const char*
进行匹配来完成。构造函数没有返回值,只有一个参数,这是类型推导所必需的。因此,需要另一个具有默认值的“虚拟参数”,它使用条件类型特征:
template<typename T> Foo(T, typename IsCharPtr<T>::Type=0)

解决方案:

#include <iostream> #define BARK std::cout << __PRETTY_FUNCTION__ << std::endl struct Dummy {}; template<typename T> struct IsCharPtr {}; template<> struct IsCharPtr<const char *> { typedef Dummy* Type; }; template<> struct IsCharPtr<char *> { typedef Dummy* Type; }; struct Foo { template<int N> Foo(const char (&)[N]) { BARK; } template<int N> Foo(char (&)[N]) { BARK; } template<typename T> Foo(T, typename IsCharPtr<T>::Type=0) { BARK; } }; const char a[] = "x"; const char* b = "x"; const char* f() { return b; } int main() { char buffer[10] = "lkj"; char* c = buffer; Foo l("x"); // Foo::Foo(const char (&)[N]) [N = 2] Foo aa(a); // Foo::Foo(const char (&)[N]) [N = 2] Foo bb(b); // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *] Foo cc(c); // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = char *] Foo ee(buffer); // Foo::Foo(char (&)[N]) [N = 10] Foo ff(f()); // Foo::Foo(T, typename IsCharPtr<T>::Type) [T = const char *] return 0; }



8
投票


2
投票

但是,这个解决方案将是特定于平台/编译器的。它不便于携带。


1
投票
static const char *

,以便程序能够访问只读内存中的文本。当声明为

const char *
时,汇编列表显示文本已从 ROM 复制到堆栈变量中。

不用担心接收者,也许可以尝试使用

static const char *

声明字符串文字。

    


1
投票

class Literal { public: explicit Literal(const char* literal) : literal_(literal) {} // The constructor is public to allow explicit conversion of external string // literals to `_L` literals. If there is no such need, then move constructor // to private section. operator const char* () { return literal_; } private: friend Literal operator"" _L (const char*, unsigned long); // Helps, when constructor is moved to private section. const char* literal_; }; Literal operator"" _L (const char* str, unsigned long) { return Literal(str); }

可以这样使用:

void f1(Literal) {} // Accepts literals only. int main() { auto str1 = "OMG! Teh Rey!"_L; std::cout << str1 << std::endl; f(str1); }

有一个缺点:您必须将 
_L

附加到每个文字 - 但这确实不是什么大问题。

    


0
投票
ansiwen

answer 更新的 C++20 版本。 请注意,就像那个答案一样,这种方法并不完美。代码中的最后一个示例将被解释为文字,即使它们不是文字。因此存在 UB 的风险。

#include <iostream> #define LITERAL std::cout << "literal" << std::endl #define NON_LITERAL std::cout << "char array" << std::endl template <typename T> concept IsCharStar = std::is_same_v<T, char*> || std::is_same_v<T, const char*>; struct Foo { template<int N> Foo(const char (&)[N]) { LITERAL; } template<int N> Foo(char (&)[N]) { NON_LITERAL; } template<int N> Foo(const char (&&)[N]) { NON_LITERAL; } template<int N> Foo(char (&&)[N]) { NON_LITERAL; } template<IsCharStar T> Foo(T) { NON_LITERAL; } }; char gchararray[] = "x"; const char* gcharstar = "x"; Foo f1() { return "hello"; } Foo f2() { return gchararray; } Foo f3() { return gcharstar; } Foo f4() { char buffer[10] = "lkj"; return buffer; } Foo f5() { const char buffer[10] = "lkj"; return buffer; } int main() { char buf[10] = "buffer"; char* charstar = "char star"; Foo v1("x"); // literal Foo v2(buf); // char array Foo v3(charstar); // char array f1(); // literal f2(); // char array f3(); // char array f4(); // char array // But note that it's not perfect const char buf2[15] = "const buffer"; Foo v4(buf2); // literal f5(); // literal, will result UB return 0; }

© www.soinside.com 2019 - 2024. All rights reserved.