区分字符串文字和运行时生成的字符串的方法

Ways to distinguish string literal and runtime generated string

本文关键字:字符串 方法 文字 运行时      更新时间:2023-10-16

假设我有一个以字符串为输入的函数:

SomeOutputType f_impl(const char* s);

大多数调用站点只使用字符串文字作为输入,例如f("Hello, world")。假设我已经实现了以下函数来计算编译时的结果

template <char...> SomeOutputType f_impl();

我的问题是,有没有办法让像f("Hello, world")这样的调用站点调用模板化表单,而像string s="Hello, world"; f(s.c_str());这样的通用调用站点调用通用表单?为了澄清,auto s = "Hello, world"; f(s);不必调用模板化表单,因为s现在是一个变量,不再是编译时常数。

这个问题的一个有用的例子是优化printf。在大多数情况下,format将是字符串文字,因此可以在编译时做很多事情来优化,而不是在运行时解析format

否,像"foo"这样的字符串文字的类型为const char[S + 1],其中S是您写入的字符数。它的行为就像一个没有特殊规则的那种类型的数组。

在C++03中,有一条特殊的规则,规定字符串文字可以转换为char*。这让你可以说

#define isStringLiteral(X) 
  isConvertibleToCharStar(X) && hasTypeConstCharArray(X)

例如,isStringLiteral(+"foo")将产生false,而isStringLiteral("foo")将产生true。即使是这种可能性也不允许您调用带有字符串文字参数的函数,并且行为不同。

C++11删除了特殊的转换规则,字符串文字的行为与任何其他数组一样。在C++11中,作为一个肮脏的黑客,你可以编写一些宏,匹配一些简单的字符串文字,而不需要处理转义序列

constexpr bool isStringLiteral(const char *x, int n = 0) {
  return *x == '"' ? 
           n == 0 ?
             isStringLiteral(x + 1, n + 1)
             : !*(x + 1) 
           : (*x && n != 0 && isStringLiteral(x + 1, n + 1));
}
#define FastFun(X) 
  (isStringLiteral(#X) ? fConstExpr(X, sizeof(X) - 1) : f(X))

虽然我还没有测试过这一点,但我认为如果您只声明函数constexpr并进行高度优化编译,编译器将尽可能在编译时进行计算。作为奖励,您不需要编写两次代码。另一方面,您必须以constexpr样式编写一次。

如果我正确理解了这个问题,我实际上认为使用函数重载可以实现这样的事情。这是一篇展示基本思想的文章。在你的情况下,我认为有以下两个过载就足够了:

void f(char const *);
template<unsigned int N>
void f(char const (&)[N]);

当字符串是字符串文字时,应该调用后者,其他时候调用后者。如果编译器足够擅长优化,那么对后者的调用可以在编译时进行评估。

编辑:

好吧,上面的解决方案不起作用让我很困扰,所以我做了一些尝试,我想我想出了一个解决方案:

#include <string>
#include <boost/utility/enable_if.hpp>
template<typename T>
struct is_string_literal {
  enum { value = false };
};
template<unsigned int N>
struct is_string_literal<char const (&)[N]> {
   enum { value = true };
};
template<typename T>
typename boost::disable_if<is_string_literal<T> >::type
foo(T) {
  std::cout << "foo1" << std::endl;
}
template<int N>
void foo(char const (&)[N]) {
  std::cout << "foo2" << std::endl;
}
int main( ) {
  std::string bar = "blah";
  char const str[] = "blah";
  foo(str);
  foo("blah");
  foo(bar.data());
}

输出(在GCC 4.4和-O3上)为:

foo2
foo2
foo1

我承认,我不完全理解为什么以前的解决方案不起作用。也许我不完全理解过载解决方案的某些方面。