2012-01-28 19 views
15

To używane do pracy kilka tygodni temu:C++ 11 constexpr funkcyjnego przekazany szablonu argumentu

template <typename T, T t> 
T   tfunc() 
{ 
    return t + 10; 
} 

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

int main() 
{ 
    std::cout << func(10) << std::endl; 
    return 0; 
} 

Ale teraz g++ -std=c++0x mówi:

main.cpp: In function ‘constexpr T func(T) [with T = int]’: 
main.cpp:29:25: instantiated from here 
main.cpp:24:24: error: no matching function for call to ‘tfunc()’ 
main.cpp:24:24: note: candidate is: 
main.cpp:16:14: note: template<class T, T t> T tfunc() 
main.cpp:25:1: warning: control reaches end of non-void function [-Wreturn-type] 

clang++ -std=c++11 mówi, że parametry szablonu z tfunc<T, t>() są ignorowane ponieważ jest nieważny.

Czy to błąd, czy naprawa?

PS

g++ --version =>g++ (GCC) 4.6.2 20120120 (prerelease)

clang++ --version =>clang version 3.0 (tags/RELEASE_30/final) (3.0.1)

+0

FWIW, Clang 3.1 HEAD również wypluwa te same błędy. – Xeo

Odpowiedz

7

Parametr t nie jest stały ekspresję. Stąd błąd. Należy również zauważyć, że nie może to być stała ekspresja.

Można przekazać wyrażenie stałe jako argument, ale wewnątrz funkcji obiekt (parametr), który przechowuje wartość, nie jest wyrażeniem stałym.

Od t nie jest stałym wyrażeniem, nie może być używany jako szablon argumentu:

return tfunc<T, t>(); //the second argument must be a constant expression 

Może chcesz coś takiego:

template <typename T, T t> 
T tfunc() 
{ 
    return t + 10; 
} 

template <typename T, T t> //<---- t became template argument! 
constexpr T func() 
{ 
    return tfunc<T, t>(); 
} 

#define FUNC(a) func<decltype(a),a>() 

int main() 
{ 
    std::cout << FUNC(10) << std::endl; 
} 

Teraz powinno działać: online demo

+0

Wciąż jestem zdezorientowany przez zdanie: * argument funkcji nie może być wyrażeniem const * –

+0

@ Mr.Anubis: Właśnie dlatego wyjaśniłem tę część w następnym paragrafie. Przeczytałeś to? – Nawaz

+0

masz na myśli '..func (T t)' wewnątrz 'func',' t' in not constexp, prawda? –

1

Wygląda na to, że powinien dać błąd - nie ma możliwości wiedząc, że przekazałeś wartość stałą jako t do func.

Ogólnie rzecz biorąc, nie można używać wartości środowiska wykonawczego jako argumentów szablonu. Szablony są z natury kompilacją czasu kompilacji.

+2

Funkcje 'constexpr' mogą być analizowane podczas kompilacji. – Xeo

+3

@Xeo Nie widzę znaczenia Twojego komentarza dla tej odpowiedzi. Wygląda na to, że mówię: "Moja ręka nie może być używana do chodzenia". i mówisz: "A twoje ramię może być używane do poruszania ręką." –

+0

rofl na analogii: D –

2

Mam wrażenie, że constexpr musi być również ważny w kontekście "środowiska wykonawczego", a nie tylko podczas kompilacji. Oznaczenie funkcji jako constexpr zachęca kompilator do próbowania oceny podczas kompilacji, ale funkcja musi mieć poprawną implementację czasu wykonywania.

W praktyce oznacza to, że kompilator nie wie, jak wdrożyć tę funkcję w czasie wykonywania:

template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T, t>(); 
} 

Rozwiązaniem jest zmiana konstruktora tak, że bierze swoją t parametr jako zwykły parametru nie jako parametr szablonu, oraz oznaczyć jako konstruktor constexpr:

template <typename T> 
constexpr T  tfunc(T t) 
{ 
    return t + 10; 
} 
template <typename T> 
constexpr T  func(T t) 
{ 
    return tfunc<T>(t); 
} 

Istnieją trzy poziomy „stałej ekspresji-Ness”:

  1. szablon Int parametrów albo (nie VLA) rozmiar tablicy // Coś musi być stała ekspresja
  2. constexpr // Coś może być stała ekspresja
  3. niestałej -expression

Nie można naprawdę przekonwertować przedmiotów znajdujących się na tej liście na coś, co znajduje się wysoko na tej liście, ale oczywiście w drugą stronę jest to możliwe.

Na przykład połączenie tej funkcji

constexpr int foo(int x) { return x+1; } 

nie jest koniecznie stała ekspresja.

// g++-4.6 used in these few lines. ideone doesn't like this code. I don't know why 
int array[foo(3)]; // this is OK 
int c = getchar(); 
int array[foo(c)]; // this will not compile (without VLAs) 

więc wartość zwracana z constexpr funkcji jest stałym wyrażeniem Tylko jeśli wszystkie parametry, a realizacja funkcji, może być zakończona w wykonany w czasie kompilacji.

+0

'constexpr int i = something_that_MUST_be_a_constexpr;' – Xeo

+0

@Xeo, musi * co najmniej * być "constexpr". Mając na uwadze, że 'template struct X; X x; 'wymaga, aby' i' był nawet bardziej niż 'constexpr'. –

+0

Może nie powinienem był używać drugiego 'constexpr' jako skrótu dla' stałej ekspresji' ... :) Chciałem tylko powiedzieć, że dla 'constexpr', czasami coś * musi * być wyrażeniem stałym - kiedy zmienna jest zadeklarowana jako taka. (Jako odpowiedź na twoje "' constexpr' // Coś, co * może * być stałym wyrażeniem ".) – Xeo

2

Podsumowanie pytania: Masz dwie funkcje, które przyjmują parametr typu T. Jeden bierze swój parametr jako parametr szablonu, a drugi jako "normalny" parametr. Mam zamiar wywołać dwie funkcje: funcT i funcN zamiast tfunc i func. Chcesz mieć możliwość dzwonienia pod numer funcT z funcN. Oznaczenie tego ostatniego jako constexpr nie pomaga.

Każda funkcja oznaczona jako constexpr musi być kompilowana tak, jakby nie było tam constexpr. constexpr funkcje są trochę schizofreniczne. W pewnych okolicznościach kończą tylko pełne wyrażenia stałe.

To nie byłoby możliwe do wdrożenia funcN do uruchomienia przy starcie w prosty sposób, jak byłoby to potrzebne, aby móc pracować dla wszystkich możliwych wartości t. Wymagałoby to kompilatora do utworzenia instancji wielu instancji tfunc, po jednej dla każdej wartości t. Ale możesz obejść to, jeśli chcesz żyć z małym podzbiorem T.Jest to szablon-rekurencji limit 1024 w g ++, dzięki czemu można z łatwością obsługiwać 1024 wartości T z tym kodem:

#include<iostream> 
#include<functional> 
#include<array> 
using namespace std; 

template <typename T, T t> 
constexpr T funcT() { 
     return t + 10; 
} 

template<typename T, T u> 
constexpr T worker (T t) { 
     return t==0 ? funcT<T,u>() : worker<T, u+1>(t-1); 

} 
template<> 
constexpr int worker<int,1000> (int) { 
      return -1; 
} 


template <typename T> 
constexpr T  funcN(T t) 
{ 
     return t<1000 ? worker<T,0>(t) : -1; 
} 

int main() 
{ 
    std::cout << funcN(10) << std::endl; 
    array<int, funcN(10)> a; // to verify that funcN(10) returns a constant-expression 
    return 0; 
} 

Wykorzystuje funkcję worker która rekurencyjnie konwersji „normalnego” parametr t do szablonu parametr u, który następnie wykorzystuje do utworzenia instancji i wykonania tfunc<T,u>.

Kluczowa linia jest return funcT<T,u>() : worker<T, u+1>(t-1);

ta ma swoje ograniczenia. Jeśli chcesz używać long lub innych typów integralnych, musisz dodać kolejną specjalizację. Oczywiście ten kod działa tylko dla t od 0 do 1000 - dokładny górny limit jest prawdopodobnie zależny od kompilatora. Innym rozwiązaniem może być użycie binarnego wyszukiwania sortuje, z inną funkcją pracownika dla każdej mocy 2:

template<typename T, T u> 
constexpr T worker4096 (T t) { 
     return t>=4096 ? worker2048<T, u+4096>(t-4096) : worker2048<T, u>(t); 

} 

myślę, że to będzie działać na całym szablonie rekurencji terminu, ale to nadal wymaga bardzo duża liczba instancji i spowolniłaby kompilację, gdyby w ogóle działała.