Предположим, у меня есть функция, которая принимает строку в качестве входных данных:
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")
дал бы истину. Даже такая возможность не позволила бы вам вызывать функцию со строковым литеральным аргументом и вести себя по-другому.
C ++ 11 удалил это специальное правило преобразования, и строковые литералы ведут себя как любые другие массивы. В C ++ 11 как грязный хак вы можете составить несколько макросов, сопоставляя несколько простых строковых литералов без обработки escape-последовательностей.
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
Я признаю, что я не совсем понимаю, почему это работает, когда предыдущее решение не сработало. Может быть, есть кое-что о разрешении перегрузки, которое я не совсем понимаю.