2015-01-07 8 views
13

C++ 14'te constexpr işlevleriyle deneme yapıyorum. beklendiği gibi faktöryel eserlerini hesaplar aşağıdaki kod,: clang şöyle derlenirC++ 14: üçlü ifadelerle Constexpr'ten çıkarılan (otomatik) dönüş türleri

template <typename T> 
constexpr auto fact(T a) { 
    if(a==1) 
     return 1; 
    return a*fact(a-1); 
} 

int main(void) { 
    static_assert(fact(3)==6, "fact doesn't work"); 
} 

:

> clang++ --version 
clang version 3.5.0 (tags/RELEASE_350/final) 
Target: x86_64-unknown-linux-gnu 
Thread model: posix 
> clang++ -std=c++14 constexpr.cpp 

Ancak, üçlü ? operatörü kullanmak fact tanımını değiştirdiğinizde:

:

template <typename T> 
constexpr auto fact(T a) { 
    return a==1 ? 1 : a*fact(a-1); 
} 

aşağıdaki derleyici hatası alıyorum Açıkça dönüş tipi T bildiririseniz Sorun şablon parametresi çıkarmak durumunda

template <typename T> 
constexpr T fact(T a) { 
    return a==1 ? 1 : a*fact(a-1); 
} 

(yerine dönüş türü anlamak için otomatik kullanarak) sabitlenir

> clang++ -std=c++14 constexpr.cpp 
constexpr.cpp:12:31: fatal error: recursive template instantiation exceeded maximum depth of 
     256 
    return a==T(1) ? T(1) : a*fact(a-1); 
     ... snip ... 
constexpr.cpp:16:19: note: in instantiation of function template specialization 'fact<int>' 
     requested here 
    static_assert(fact(3)==6, "fact doesn't work"); 

, desen (üçlü versiyonu tekrarlanır başarısız olur ve if versiyonu çalışır) bu oysa

// this works just fine 
constexpr auto fact(int a) { 
    if(a==1) 
     return 1; 
    return a*fact(a-1); 
} 

constexpr auto fact(int a) { 
    return a==1 ? 1 : a*fact(a-1); 
} 
başarısız Aşağıdaki hata ile

> clang++ -std=c++14 constexpr.cpp 
constexpr.cpp:16:25: error: function 'fact' with deduced return type cannot be used before it 
     is defined 
    return a==1 ? 1 : a*fact(a-1); 
         ^
constexpr.cpp:15:16: note: 'fact' declared here 
constexpr auto fact(int a) { 
      ^
constexpr.cpp:20:26: error: invalid operands to binary expression ('void' and 'int') 
    static_assert(fact(3)==6, "fact doesn't work"); 
        ~~~~~~~^ ~ 
2 errors generated. 

Burada neler oluyor?

cevap

11

Üçlü bir ifadeyi değerlendiren sonuç türü common type of its second and third arguments'dur.

Derleyicinin dönüş türünü almasını sağlayarak, bu bağımsız değişkenlerin her ikisini de üçlü ifadeye değerlendirmek için zorlarsınız. Bu, sonlandırma koşulu elde edildiğinde bile yineleme sona ermediği anlamına gelir, çünkü fact(0) geri dönüş türünü bulmak için, derleyici, fact'a tekrar eden özyinelemeli aramaları değerlendirmeye devam etmeli ve sonsuz yineleme devam etmelidir.

İade türünü belirterek, fact(0)'un a==1 olduğunda değerlendirilmesi gerekli değildir ve yineleme sonlandırılabilir. İki return ifadelere durum için de


, ilgili standart maddesi (N4296 adlı)

— olan §7.1.6.4/9[dcl.spec.auto]

If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.

Örneğinizde, fact<int>(1) numaralı çağrıda, ilk return ifadesinden çıkarılan döndürme türünün değeri'dir., yani fact<int>(0) ikinci return deyiminde dönüş türü, int dışında bir şey olamaz. Bu, derleyicinin fact<int>(0) gövdesini değerlendirmesine gerek olmadığı ve yinelemenin sonlanabileceği anlamına gelir.

Gerçekten de, T şablon argümanı

template <unsigned T> 
constexpr auto fact() { 
    if(T==1) 
     return 1; 
    return T*fact<T-1>(); 
} 

clang başarısız yapar olmayan bir türüdür böylece ilk örneği değiştirerek, örneğin, hem de ikinci return açıklamada fact çağrısına değerlendirmesini zorlarsanız hata

fatal error: recursive template instantiation exceeded maximum depth of 256

Live demo

+0

sürümü iki dönüş ifadeleri varsa ile. Derleyici kullanımı/standardı bir tür temkinli boşalma belirtiyor mu, sadece bir == 1 olduğunda if bloğunda dönüş ifadesini dikkate alıyor? Aksi takdirde, her iki versiyonda da aynı problem olurdu. – bcumming

+0

Teşekkür @Praetorian. Kendime bakabilirim (bir dil avukatım olmasa da). – bcumming

+0

@Praetorian Daha önce iddia ettiğiniz şeylerin tam tersi değil. Her bir getiri beyanı için getiri türü çıkarılır, ancak * herhangi bir getiri beyanı için çıkarıldıktan sonra, getiri türü bilinir ve daha sonraki getiri beyannamelerinin indirilmesinin bir parçası olarak kullanılabilir. 'Template 'örneğiyle başarısız olmanızın nedeni," aslında "türünün dönüş türünün bilinmesi gerçeği" aslında "dönüş türüyle ilgili hiçbir şey söylemez, ancak' ' aslında OP kodunda "aslında " olarak adlandırılır. . – hvd