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

时间:2013-02-17 20:20:07

标签: c++ templates c++11 string-literals

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

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

3 个答案:

答案 0 :(得分: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))

答案 1 :(得分:0)

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

答案 2 :(得分:-2)

如果我正确地理解了这个问题,我实际上认为使用函数重载可以做到这一点。显示基本想法的Here's an article。在你的情况下,我认为有两个重载是足够的:

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

我承认,当前一个解决方案没有时,我并不完全理解为什么会这样。也许有一些关于重载分辨率的东西我还不完全理解。