Konkretyzacja szablonu wskaznikiem - zagadka

0

Taka ciekawostka:

#include <iostream>
using namespace std;

template <const int* WSKAZNIK>
const int* Funkcja () {
    return WSKAZNIK;
    }

extern const int tablica_jeden [ 1 ] = { 1 };
const int tablica_dwa [ 1 ] = { 2 };
int tablica_trzy [ 1 ] = { 3 };

int main(){
  cout << Funkcja < tablica_jeden > () << endl; // działa
  //cout << Funkcja < tablica_dwa > () << endl; // nie działa
  cout << Funkcja < tablica_trzy > () << endl; // działa
}

I pytania:

  1. Czemu różnica extern (pierwsza tablica) powoduje działanie, podczas gdy jego brak (druga tablica nie)?
  2. Czemu działa z tablicą trzecią, mimo, że brak stałości.
5

const implikuje internal linkage w C++ (w C nie).

static int tablica_cztery[1] = {4}; // err
static const int tablica_piec[1] = {5}; // err
static constexpr int tablica_szesc[1] = {6}; // ok

W sumie ciekawe jakie są faktyczne techniczne powody tego stanu rzeczy.

Luźne myśli:
Na pierwszy rzut oka jest to prewencja błędów - tablice const z internal linkage mogą być w założeniu stałymi kompilacji niewrzuconymi do sekcji danych przez kompilator (po angielsku ładnie się na to mówi "optimized out") - a pobranie ich adresu generuje kopię w każdym TU, w którym adres pobrano. Konkretyzacja szablonu istniałaby w tym momencie dla każdego z tych adresów (o ile została w danym TU użyta - a tak by było w przypadku konkretyzacji w nagłówku).

W przypadku zmiennych o statycznym czasie życia, standard jedynie żąda aby zostały zainicjalizowane przed pierwszym użyciem więc kompilator nie musi znać ich adresu w czasie kompilacji.

0

Ok - dzięki. To wyjaśnia kwestię tablicy_dwa. Ale nie wyjaśnia działania dla tablicy_jeden i tablicy_trzy

0

A nawet mam jeszcze fajniejszy przykładzik:

#include <iostream>
using namespace std;
 
template <const int* WSKAZNIK>
const int* Funkcja () {
      return WSKAZNIK;
      }
 
extern const int tablica_jeden [ 1 ] = { 1 };
const int tablica_dwa [ 1 ] = { 2 };
int tablica_trzy [ 1 ] = { 3 };

extern const int* wskaznik_jeden = tablica_jeden;
 
int main(){
    cout << Funkcja < tablica_jeden > () << endl; // działa
    //cout << Funkcja < tablica_dwa > () << endl; // nie działa
    cout << Funkcja < tablica_trzy > () << endl; // działa

    //cout << Funkcja < wskaznik_jeden > () << endl; // TUTAJ - jeszcze ciekawsze, bo nie działa
}
1

Tutaj to akurat nic dziwnego, wartość wskaźnika nie jest znana w czasie kompilacji. Z constexpr powinno działać.

3

Po dyskusji¹ z @Endrju @satirev i @adrian17 "odkryliśmy", że od C++11 ten kod się powinien kompilować, ponieważ standard obniżył wymagania i zezwala aby "non-type, non-template template arguments" miały internal linkage.

Clang to przepuszcza, gcc ma zgłoszonego buga: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52036
msvc nie wiem, ale znając życie blokuje połowę przypadków bez ładu i składu.

¹ poprzez IRC oczywiście, IRC jest najlepszy do technicznych rozmów.

1

msvc nie wiem, ale znając życie blokuje połowę przypadków bez ładu i składu.

VS2015 kompiluje bez ostrzeżeń.

1 użytkowników online, w tym zalogowanych: 0, gości: 1