Подтвердить что ты не робот

Строки C-Style в качестве аргументов шаблона?

Можно ли использовать строки C-Style в качестве аргументов шаблона?

Я пробовал:

template <char *str>
struct X
{
    const char *GetString() const
    {
         return str;
    }
};

int main()
{
    X<"String"> x;
    cout<<x.GetString();
}

И хотя я не получаю жалобы на определение класса, экземпляр дает 'X' : invalid expression as a template argument for 'str' (VC).

4b9b3361

Ответ 1

Строковый литерал не может использоваться как аргумент шаблона.

Обновление. В настоящее время, спустя несколько лет этот вопрос задавался и отвечал, можно использовать строковые литералы в качестве аргументов шаблона. С С++ 11 мы можем использовать пакеты символов в качестве аргументов шаблона (template<char ...c>), и это возможно , чтобы передать литеральную строку в такой шаблон.

Это будет работать, однако:

template <char const *str>
struct X
{
    const char *GetString() const
    {
         return str;
    }
};

char global_string[] = "String";

int main()
{
    X<global_string> x;
    cout<<x.GetString();
}

Ответ 3

Я знаю, эта тема немного устарела, но я помещаю этот комментарий, если кому-то интересно. Я получил шаблоны с передачей литерала в качестве аргумента с комбинацией MACROS.

Я сделал пример кода,

#include <stdio.h>
#include <iostream>
#include <vector>
#include <memory>
#include <string.h>

using namespace std;

#define MAX_CONST_CHAR 100

#define MIN(a,b) (a)<(b)?(a):(b)

#define _T(s)\
getChr(s,0),\
getChr(s,1),\
getChr(s,2),\
getChr(s,3),\
getChr(s,4),\
getChr(s,5),\
getChr(s,6),\
getChr(s,7),\
getChr(s,8),\
getChr(s,9),\
getChr(s,10),\
getChr(s,11),\
getChr(s,12),\
getChr(s,13),\
getChr(s,14),\
getChr(s,15),\
getChr(s,16),\
getChr(s,17),\
getChr(s,18),\
getChr(s,19),\
getChr(s,20),\
getChr(s,21),\
getChr(s,22),\
getChr(s,23),\
getChr(s,24),\
getChr(s,25),\
getChr(s,26),\
getChr(s,27),\
getChr(s,28),\
getChr(s,29),\
getChr(s,30),\
getChr(s,31),\
getChr(s,32),\
getChr(s,33),\
getChr(s,34),\
getChr(s,35),\
getChr(s,36),\
getChr(s,37),\
getChr(s,38),\
getChr(s,39),\
getChr(s,40),\
getChr(s,41),\
getChr(s,42),\
getChr(s,43),\
getChr(s,44),\
getChr(s,45),\
getChr(s,46),\
getChr(s,47),\
getChr(s,48),\
getChr(s,49),\
getChr(s,50),\
getChr(s,51),\
getChr(s,52),\
getChr(s,53),\
getChr(s,54),\
getChr(s,55),\
getChr(s,56),\
getChr(s,57),\
getChr(s,58),\
getChr(s,59),\
getChr(s,60),\
getChr(s,61),\
getChr(s,62),\
getChr(s,63),\
getChr(s,64),\
getChr(s,65),\
getChr(s,66),\
getChr(s,67),\
getChr(s,68),\
getChr(s,69),\
getChr(s,70),\
getChr(s,71),\
getChr(s,72),\
getChr(s,72),\
getChr(s,72),\
getChr(s,73),\
getChr(s,74),\
getChr(s,75),\
getChr(s,76),\
getChr(s,77),\
getChr(s,78),\
getChr(s,79),\
getChr(s,80),\
getChr(s,81),\
getChr(s,82),\
getChr(s,83),\
getChr(s,84),\
getChr(s,85),\
getChr(s,86),\
getChr(s,87),\
getChr(s,88),\
getChr(s,89),\
getChr(s,90),\
getChr(s,91),\
getChr(s,92),\
getChr(s,93),\
getChr(s,94),\
getChr(s,95),\
getChr(s,96),\
getChr(s,97),\
getChr(s,98),\
getChr(s,99),\
getChr(s,100)

#define getChr(name, ii) ((MIN(ii,MAX_CONST_CHAR))<strlen(name)?name[ii]:0)

template <char... Chars_>
 class E {

    public:
    string *str;

    E(){
        std::vector<char> vec = {Chars_...};
        str = new string(vec.begin(),vec.end());
    }

    ~E()
     {
        delete str;
     }
 };

int main(int argc, char *argv[])
{

    E<_T("Any template can pass const strings literals")> e;

    printf("%s",e.str->c_str());

}

Это работает с g++ 4.6 и передает аргумент -std = С++ 0x и имеет предел 100 char, но, конечно, может быть как можно больше. Возможно, этот метод не очень оптимизирован, но он будет более продуктивным, чем объявление необходимых внешних переменных (я уверен;))

Ограничения: буквальная строка должна быть одним и последним аргументом шаблона из-за прохождения аргументов переменной.

Ответ 4

Извините, что публиковал такой старый вопрос, но вот то, что я чувствую, является самым чистым подходом к тому, чтобы фактически передавать литерал как аргумент без использования хранилища.

Кодировать строку как тип:

template <char... chars>
using tstring = std::integer_sequence<char, chars...>;

Создайте пользовательский оператор литерала:

template <typename T, T... chars>
constexpr tstring<chars...> operator""_tstr() { return { }; }

И используйте частичную специализацию для восстановления символьных данных по мере необходимости:

template <typename>
struct X;

template <char... elements>
struct X<tstring<elements...>> {
    const char* GetString() const
    {
        static constexpr char str[sizeof...(elements) + 1] = { elements..., '\0' };
        return str;
    }
};

Это позволяет вам написать:

X<decltype("my_string"_tstr)>

Пользовательский литерал использует нестандартную (n3599) функциональность не в С++ 14, но она поддерживается недавними GCC и Clang строит и, надеюсь, будет пересмотрен для С++ 1z.

Ответ 5

Нет, вы не можете работать со строковыми литералами во время компиляции. Лучшее, что вы можете получить, это странные многоканальные литералы (например, 'abcd'), которые используют некоторые анализаторы времени компиляции. Они упоминаются в п. 2.2.13.2.1:

Обычный символ буква, который содержит более одного c- char, является многоканальный литерал. Многохарак- ter имеет тип int и значение, определяемое реализацией.

В С++ 0x могут быть способы обойти эти ограничения, но с новыми строковыми литералами, Arctic Interactive содержит интересную статью.

Ответ 6

С С++ 11 вы можете довольно корректно представлять строковые литералы в виде вариационных аргументов шаблона, т.е. набор параметров шаблона int. Я собрал доказательство примера концепции, который устанавливает один такой шаблон без необходимости вручную писать foo<16, 73, 51 ...> для каждой такой строки.

Пример:

// The template we want to pass a string to
template <int... Args>
struct foo {
  // It needs one helper function for decltype magic, this could be avoided though
  template <int N>
  static foo<N, Args...>  add_one();
};

// This is the string we want to use with foo, simulating foo<"Hello world!" __FILE__>:
constexpr const char *teststr = "Hello world!" __FILE__;

// Get char N of a string literal
constexpr int strchr(const char *str, int N) { return str[N]; }

// recursive helper to build the typedef from teststr
template <int N, int P=0>
struct builder {
   typedef typename builder<N, P+1>::type child;
   typedef decltype(child::template add_one<strchr(teststr,P)>()) type;
};

template <int N>
struct builder<N,N> {
  typedef foo<strchr(teststr, N)> type;
};

// compile time strlen
constexpr int slen(const char *str) {
  return *str ? 1 + slen(str+1) : 0;
}

int main() {
  builder<slen(teststr)>::type test;
  // compile error to force the type to be printed:
  int foo = test;
}

Вам понадобится хотя бы gcc 4.6 для constexpr, и он может использовать некоторый польский, но ошибка компилятора, которую я получаю, указывает, что тип строится безопасно:

error: cannot convert ‘builder<19>::type {aka foo<72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 115, 108, 105, 116, 46, 99, 99, 0>}’ to ‘int’ in initializatio

Ответ 7

Нет. Согласно стандарту С++ 14.3.2/1:

Аргумент шаблона для непигового шаблона-шаблона без шаблона должен быть одним из следующих:
- интегральное постоянное выражение интегрального или перечисляемого типа; или
- имя несимметричного шаблона; или
- адрес объекта или функции с внешней связью, включая шаблоны функций и идентификаторы шаблонов функций, но исключая нестатические члены класса, выраженные как и id-expression, где и является необязательным, если имя ссылается на функцию или массив, или если соответствующий шаблон-параметр является ссылкой, или - указатель на элемент, выраженный как описано в 5.3.1.

Строки отсутствуют в списке.

Ответ 8

Вы можете использовать адрес строки с внешней привязкой в ​​качестве параметра шаблона, например:

template <const char** T> class My {
public:
    void do_stuff() {
      std::cout << "zzz";
    }
};

const char* str;

int main()
{
  My<&str> zz;
    zz.do_stuff();

    printf("Result: %d %d \n",  60 % 10 + 1, (60 % 10 ) + 1 );
}

Ответ 9

С++ не знает о строках. Он только знает о "массивах символов", и там литерал будет указателем на массив. Поэтому, если вы будете использовать "значение" вашей строки в качестве параметра шаблона, вы бы фактически использовали значение указателя.