2017-01-02 40 views
14

I zostały wdrożone constexpr tablicę tak:Nie można skonstruować constexpr tablicę z usztywnione-init-listy

template <typename T> 
class const_array { 
    const T* p; 
    unsigned n; 
public: 
    template <unsigned N> 
    constexpr const_array(const T(&a)[N]): p(a), n(N) { } 

    constexpr unsigned size() const { return n; } 
}; 

int main(int argc, char* argv[]) { 
    // works 
    static_assert(const_array<double>{{1.,2.,3.}}.size() == 3); 

    // doesn't compile 
    constexpr const_array<double> a{{1.,2.,3.}}; 
    static_assert(a.size() == 3); 
} 

Dlaczego jest to, że pierwszy static_assert kompiluje, ale inicjowanie a nie używam gcc 6.2? .0. Dostaję

: In function 'int main(int, char**)': 
: error: 'const_array<double>{((const double*)(&<anonymous>)), 3u}' is not a constant expression 
    constexpr const_array<double> a{{1.,2.,3.}}; 
             ^
test/const_array.cc:17:3: error: non-constant condition for static assertion 
    static_assert(a.size() == 3); 
    ^~~~~~~~~~~~~ 
+1

Zdefiniowałeś 'a' dwa razy. Wygląda na to, że może się zdarzyć niejawna konwersja. Czy otrzymasz jakąkolwiek poprawę, jeśli udekorujesz c'tora w sposób wyraźny? – KyleKnoepfel

+0

@KyleKnoepfel Tworzenie wyraźnego konstruktora nie pomaga. – SU3

+1

'a1' kończy się niepowodzeniem w klangu z' wskaźnikiem do podobiektu tymczasowego nie jest wyrażeniem stałym'. – jbapple

Odpowiedz

11

Kompilator narzeka, że ​​inicjator od a.p nie jest stałą ekspresję. Zawodzi: § 5.20/5.2:

jeśli wartość jest typu wskaźnika, zawiera adres obiektu z czasem przechowywania statycznego, adres znajdujący się za końcem takiego obiektu (5.7), adres funkcja lub wartość wskaźnika pustego

Innymi słowy, tylko wartości wskaźników znane linkerowi są poprawnymi stałymi. (Również w przykładzie wskaźnik jest zwisające.)

Pierwszy static_assert nie potknąć tego powodu p odrzuca się, a wartość n jest stałą ekspresję. Wyrażenia stałe mogą mieć nieciągłe podwyrażenia.

to działa:

static constexpr double arr[] = { 1.,2.,3. }; 
constexpr const_array<double> a{ arr }; 
static_assert(a.size() == 3); 

kredytową do @ Jarod42 za wskazanie problemu w komentarzach.

+1

Nie jestem pewien, czy ta sekcja ma zastosowanie. Nie mamy wskaźnika prvalue w dowolnym miejscu. Zarówno gcc jak i clang pozwalają 'static_assert (const_array {{1., 2., 3.}}. P [0] == 1.)' (zakładając, że piszemy 'p' publicznie) – Barry

+0

@Barry Ponieważ wartość 'a' jest wyrażeniem stałym, stosuje się punktator 3:" jeśli wartość jest obiektem klasy lub typu tablicy, każdy podobiekt spełnia te ograniczenia dla wartości. " 'a.p' jest podobiektiem' a', więc stosuje się do niego punktor 2. W twoim 'static_assert' warunek' == 1' jest wyrażeniem stałym, ale ' .p' jest tylko jego podwyrażeniem, które nie jest dozwolone przez siebie jako wyrażenie stałe. – Potatoswatter

+0

Ale "a" nie jest wartością pryncypium. – Barry

0

Problem polegał na tym, że nie można przesyłać stałej natury wskaźnika do T w szablonie wewnętrznym za pomocą parametru T szablonu zewnętrznego.

template <typename T> class const_array { 
    const T * p; 
    unsigned n; 
public: 
    template <unsigned N> 
     constexpr const_array(const T(& a)[N]): p(a), n(N) { } 
}; 

int main(int argc, char* argv[]) { 
    constexpr const_array<double> ca{(const double []) { 1., 2. }}; 
    return 0; 
} 

Próbowałem kilkadziesiąt permutacje pozbyć obsady bez powodzenia.

+1

To nie odpowiada na pytanie. – Barry

+0

@ Barry, tak to robi. Oba błędy w pytaniu były związane z tym, że wskaźnik do T nie jest stały w zewnętrznym szablonie, więc kompilator nie może znaleźć wewnętrznego szablonu i nie ma konstrukcji gwarantującej ciągłość wskaźnika do T w przejściu T. Byłoby niewłaściwe (zły hack), aby to zrobić w zewnętrznym szablonie tylko po to, aby wymusić dopasowanie. – FauChristian

+0

@FauChristian "Both"? W pytaniu jest jeden błąd. – Barry