Literały ciągów niedozwolone jako parametry szablonu non type

Poniższy cytat pochodzi z C++ Templates by Addison Wesley . Czy ktoś mógłby mi pomóc zrozumieć w zwykły angielski / laik terminy{[2] } jego istotę?

Ponieważ literały łańcuchowe są obiektami z wewnętrznym połączeniem (dwa literały łańcuchowe o tej samej wartości, ale w różnych modułach są różnymi obiektami), nie można ich używać jako argumentów szablonu:

Author: sbi, 2011-04-05

5 answers

Twój kompilator ostatecznie działa na rzeczach zwanych jednostki tłumaczeniowe , nieformalnie nazywane plikami źródłowymi. W ramach tych jednostek tłumaczeniowych identyfikuje się różne byty: obiekty, funkcje itp. Zadaniem linkerów jest połączenie tych jednostek ze sobą, a częścią tego procesu jest scalanie tożsamości.

Identyfikatory mają powiązania: połączenie wewnętrzne oznacza, że encja nazwana w tej jednostce tłumaczenia jest widoczna tylko do tej jednostki translacyjnej, podczas gdy zewnętrzne powiązanie oznacza, że jednostka jest widoczna dla innych jednostek.

Gdy encja jest oznaczona static, otrzymuje się wewnętrzne powiązanie. Tak więc biorąc pod uwagę te dwie jednostki tłumaczeniowe:

// a.cpp
static void foo() { /* in a */ } 

// b.cpp
static void foo() { /* in a */ } 

Każdy z tych foo odnosi się do jednostki (funkcji w tym przypadku), która jest widoczna tylko dla ich odpowiednich jednostek tłumaczeniowych; to znaczy, każda jednostka tłumaczeniowa ma swoją własną foo.

Tutaj jest haczyk, więc: literały łańcuchów są takie same wpisz jako static const char[..]. Czyli:

// str.cpp
#include <iostream>

// this code:

void bar()
{
    std::cout << "abc" << std::endl;
}

// is conceptually equivalent to:

static const char[4] __literal0 = {'a', 'b', 'c', 0};

void bar()
{
    std::cout << __literal0 << std::endl;
}

I jak widzisz, wartość literała jest wewnętrzna do tej jednostki tłumaczenia. Więc jeśli użyjesz "abc" w wielu jednostkach tłumaczeniowych, na przykład, wszystkie one będą różnymi jednostkami.

Ogólnie rzecz biorąc, oznacza to, że jest to pojęciowe bez znaczenia:

template <const char* String>
struct baz {};

typedef baz<"abc"> incoherent;

Ponieważ "abc" jest Inna dla każdej jednostki tłumaczenia. Każda jednostka tłumaczenia otrzymałaby inną klasę, ponieważ każda "abc" jest inną encji, mimo że podali" ten sam " argument.

Na poziomie języka jest to narzucone przez stwierdzenie, że parametry szablonu nie-type mogą być wskaźnikami do encji z zewnętrznym linkiem ; to znaczy rzeczy, które robią odnoszą się do tego samego encji w różnych jednostkach tłumaczeniowych.

Więc to jest w porządku:

// good.hpp
extern const char* my_string;

// good.cpp
const char* my_string = "any string";

// anything.cpp
typedef baz<my_string> coherent; // okay; all instantiations use the same entity

†nie wszystkie identyfikatory mają powiązania; niektóre nie mają żadnych, takich jak parametry funkcji.

‡ kompilator optymalizujący będzie przechowywał identyczne literały pod tym samym adresem, aby zaoszczędzić miejsce; ale to jakość wykonania, a nie gwarancja.

 45
Author: GManNickG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:54:25

To znaczy, że nie możesz tego zrobić...

#include <iostream>

template <const char* P>
void f() { std::cout << P << '\n'; }

int main()
{
    f<"hello there">();
}

...ponieważ "hello there" nie jest w 100% gwarantowane rozdzielenie do pojedynczej wartości całki, która może być użyta do utworzenia instancji szablonu raz (chociaż większość dobrych linkerów spróbuje złożyć wszystkie zastosowania w połączonych obiektach i wytworzyć nowy obiekt z pojedynczą kopią ciągu znaków).

Można jednak używać zewnętrznych tablic znaków/wskaźników:

...
extern const char p[];
const char p[] = "hello";
...
    f<p>();
...
 11
Author: Tony Delroy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-05 06:18:58

Oczywiście literały typu string, takie jak" foobar", nie są podobne do innych wbudowanych typów literałów (takich jak int czy float). Muszą mieć adres (const char*). Adres jest tak naprawdę stałą wartością, którą kompilator zastępuje w miejscu, w którym pojawia się literał. Adres ten wskazuje na jakieś, ustalone w czasie kompilacji, miejsce w pamięci programu.

Z tego powodu musi mieć wewnętrzne powiązanie. Wewnętrzne powiązanie oznacza tylko, że nie można połączyć między jednostkami tłumaczeniowymi (skompilowany cpp plików). Kompilator może próbować to zrobić, ale nie jest to wymagane. Innymi słowy, wewnętrzne powiązanie oznacza, że jeśli weźmiesz adres dwóch identycznych ciągów literalnych (tj. wartość znaku const*, na który tłumaczą) w różnych plikach cpp, nie będą one zasadniczo takie same.

Nie można ich używać jako parametrów szablonu, ponieważ wymagałyby one strcmp (), aby sprawdzić, czy są takie same. Jeśli użyjesz==, po prostu porównasz adresy, które nie będą takie same gdy szablon jest tworzony z tym samym ciągiem literalnym w różnych jednostkach tłumaczenia.

Inne prostsze typy wbudowane, jako literały, są również linkami wewnętrznymi (nie mają identyfikatora i nie mogą być połączone ze sobą z różnych jednostek tłumaczeniowych). Jednak ich porównanie jest trywialne, ponieważ jest to wartość. Więc mogą być używane do szablonów.

 7
Author: Mikael Persson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-05 06:28:04

Jak wspomniano w innych odpowiedziach, literał Łańcuchowy nie może być używany jako argument szablonu. Istnieje jednak obejście, które ma podobny efekt, ale "ciąg" jest ograniczony do czterech znaków. Wynika to z stałych wieloznakowych, które, jak zostało omówione w linku, są prawdopodobnie raczej nie do przeniesienia, ale działały dla moich celów debugowania.

template<int32_t nFourCharName>
class NamedClass
{
    std::string GetName(void) const
    {
        // Evil code to extract the four-character name:
        const char cNamePart1 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*3) & 0xFF);
        const char cNamePart2 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*2) & 0xFF);
        const char cNamePart3 = static_cast<char>(static_cast<uint32_t>(nFourCharName >> 8*1) & 0xFF);
        const char cNamePart4 = static_cast<char>(static_cast<uint32_t>(nFourCharName       ) & 0xFF);

        std::ostringstream ossName;
        ossName << cNamePart1 << cNamePart2 << cNamePart3 << cNamePart4;
        return ossName.str();
    }
};

Może być używany z:

NamedClass<'Greg'> greg;
NamedClass<'Fred'> fred;
std::cout << greg.GetName() << std::endl;  // "Greg"
std::cout << fred.GetName() << std::endl;  // "Fred"

Jak powiedziałem, to jest obejście. Nie udaję, że to dobry, czysty, przenośny Kod, ale innym może się to przydać. Inne obejście może obejmować wiele argumentów szablonu char, jak w ta odpowiedź .

 3
Author: Diamond Python,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:15

Idea standardu c++ zezwalającego tylko na określony typ parametrów szablonów polega na tym, że parametr powinien być stały i znany w czasie kompilacji w celu wygenerowania kodu "specialized class".

W tym konkretnym przypadku: Kiedy tworzysz ciąg znaków literalnych, ich adres jest Nieznany do czasu łączenia (łączenie odbywa się po kompilacji), ponieważ dwa literały ciągów w różnych jednostkach tłumaczenia są dwoma różnymi obiektami (co doskonale wyjaśnia przyjęta odpowiedź). Podczas kompilacji zdarza się, że nie wiemy, którego adresu string dosłownego użyć do wygenerowania specjalistycznego kodu klasy z klasy template.

 0
Author: PnotNP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-15 01:28:01