-4

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

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在运行时解析。

4

3 回答 3

17

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

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

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

例如isStringLiteral(+"foo")会产生false,并且isStringLiteral("foo")会产生真。即使这种可能性也不允许您使用字符串文字参数调用函数并表现不同。

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))
于 2013-02-17T20:24:01.307 回答
0

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

于 2013-02-18T18:36:16.017 回答
-2

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

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

输出(在带有 -O3 的 GCC 4.4 上)是:

foo2
foo2
foo1

我承认我不完全理解为什么当以前的解决方案不起作用时它会起作用。也许有一些关于重载决议的东西我不完全理解。

于 2013-02-17T20:45:35.627 回答