2015-07-30 13 views
8

Say mam klasy, że może wrócić do stałej ekspresji poprzez constexpr funkcję:C++ 14: Inicjowanie zmiennych constexpr od parametru wartości

template<int N> 
struct Foo { 
    constexpr int Bar() const { return N; } 
}; 

Gdybym chciał zainicjować wartości constexpr z Foo::Bar(), jak powinny Przekazuję parametr typu Foo? Próbowałem te dwa, na przykładzie constexpr zmienną wewnątrz siebie, aby sprawdzić, czy to może być inicjowane:

template<int N> 
constexpr int ByValue(Foo<N> f) { 
    constexpr int i = f.Bar(); 
    return f.Bar(); 
} 

template<int N> 
constexpr int ByReference(const Foo<N> &f) { 
    constexpr int i = f.Bar(); 
    return f.Bar(); 
} 

constexpr int a = ByValue(Foo<1>{}); 
constexpr int b = ByReference(Foo<1>{}); 

Ale Clang 3.7 podnosi błąd na ByReference podczas gcc> = 5,1 nie: Live demo

main.cpp:15:25: error: constexpr variable 'i' must be initialized by a constant expression 
     constexpr int i = f.Bar(); 
         ^~~~~~~ 
main.cpp:22:25: note: in instantiation of function template specialization 'ByReference<1>' requested here 
     constexpr int b = ByReference(Foo<1>{}); 

Jaka jest różnica pomiędzy podjęciem const Foo & lub zwykły Foo, gdy Bar jest constexpr albo sposób i zwraca poprawną stałą ekspresję?

Co jest słuszne i dlaczego, GCC lub Clang? Jeśli to możliwe, mile widziane będą odniesienia do normy.

+0

Istnieje pewien głupi język na temat odniesień w wyliczaniu standardu, co może dać kompilację wyrażenia w postaci stałej czasowej. Najwyraźniej klang pedantycznie honoruje ten język, podczas gdy g ++ bardziej praktycznie go lekceważy. Mam nadzieję, że zostanie to naprawione, ale nie mogę sobie przypomnieć, widząc jakikolwiek raport o usterce na ten temat. Upshot: jest to szara strefa i może nadal nią być. Użyj makr, aby zdefiniować ogólną funkcję rozmiaru tablicy, po prostu po bezpiecznej stronie. –

+1

Musisz sprawić, by twoja funkcja członkowska była "statyczna", tak aby nie zależała od niejawnego "tego" wskaźnika i może dyskwalifikuje się z bycia stałym wyrażeniem. – CoffeeandCode

+0

Nie mam czasu, aby dać odpowiedź teraz, ale to jest podobne do [to pytanie] (http://stackoverflow.com/q/31375381/1708801) –

Odpowiedz

5

§5.20:

enter image description here

Odniesienie nie posiada poprzedni inicjalizacji z punktu widzenia i, ale: Jest to parametr. Został zainicjowany po wywołaniu ByReference.

Niech usunąć constexpr z deklaracją i „s i rozważyć wywołanie ByReference w całości:

template<int N> 
constexpr int ByReference(const Foo<N> &f) { 
    int i = f.Bar(); 
    return i; 
} 

constexpr int j = ByReference(Foo<0>()); 

Jest to dobre rozwiązanie, ponieważ f ma poprzedzający inicjalizacji. Inicjator f jest również wyrażeniem stałym, ponieważ domyślnie deklarowany domyślny konstruktor to w tym przypadku constexpr (§12.1/5).
Dlatego i jest inicjowane przez wyrażenie stałe, a wywołanie to samo wyrażenie stałe.

+0

, więc sugerujesz, że klang jest zły? – Walter

+1

@Walter No. Clang odrzuca go, a więc to prawda. Zgodnie z obecnymi przepisami. – Columbo

+0

Jestem fanem tego zrzutu ekranu/najważniejszego podejścia do cytowania. +1! – Barry