G++ i clang++ różne zachowania z integralnym parametrem szablonu

Mam następujący kod C++11.

#include <type_traits>

using IntType = unsigned long long;

template <IntType N> struct Int {};

template <class T>
struct is_int : std::false_type {};

template <long long N>
struct is_int<Int<N>> : std::true_type {};

int main()
{
    static_assert (is_int<Int<0>>::value, "");
    return 0;
}

Clang++ 3.3 kompiluje kod, ale w g++ 4.8.2 twierdzenie statyczne nie powiodło się

$ g++ -std=c++11 main.cpp 
main.cpp: In function ‘int main()’:
main.cpp:15:5: error: static assertion failed: 
     static_assert (is_int<Int<0>>::value, "");
     ^
$ 

Problem jest spowodowany przez różne integralne parametry szablonu. Który kompilator ma rację w tym przypadku?

Author: sawyer, 2013-11-28

2 answers

Niespodzianka

To subtelny pluskwa, głęboko zakopana w normie. Problem polega na tym, że w prawie wszystkich przypadkach argumenty nie będące typem szablonu mogą być przekonwertowane na typ parametru szablonu . Na przykład wyrażenie Int<0> ma int dosłowny argument wartości 0, który jest konwertowany na typ unsigned long long parametru szablonu N.

14.8.2 szablon argument dedukcja [temp.

-- Non-type arguments must match the types of the corresponding non-type parametry szablonu, lub muszą być zamieniane na typy odpowiednie parametry inne niż typ określone w pkt 14.3.2, w przeciwnym razie odliczenie typu nie powiodło się.

Ponieważ szablon klasy is_int<T> ma częściową specjalizację, musimy spojrzeć na

14.5.5.1 dopasowanie szablonu klasy specjalizacje cząstkowe [temp.klasy.spec.mecz]

1 gdy Klasa szablon jest używany w kontekście wymagającym instancjacji klasy, konieczne jest ustalenie, czy instancjację należy wygenerować za pomocą szablonu podstawowego lub jednego z częściowe specjalizacje. odbywa się to poprzez dopasowanie szablonu argumenty specjalizacji szablonu klasy z szablonem lista argumentów częściowych specjalizacji .

2 częściowa specjalizacja pasuje do danego argumentu szablonu lista jeśli szablon argumenty częściowej specjalizacji mogą być wyprowadzony z rzeczywistej listy argumentów szablonu (14.8.2).

Wydaje się więc, że możemy przejść do wcześniejszego cytatu z 14.8.2 / 2 2nd bullet i dopasować drugą specjalizację (chociaż w tym przypadku trzeba by zagrać jeszcze bardziej skomplikowaną grę o rozdzielczości przeciążenia).

Uchwała

Okazuje się jednak (jak wspomniał @DyP w komentarzach), że kolejna klauzula w standardzie zastępuje to:

14.8.2.5 wyprowadzenie argumentów szablonu z typu [temp.odlicz.Typ]

17 jeśli w deklaracji szablonu funkcji o nie-typie template-parametr, nie-typowy templateparametr jest używany w wyrażenie w parametrze funkcji-list i, jeśli odpowiadający szablon-argument jest wyprowadzany, typ szablonu-argument powinien odpowiadać typ szablonu-parametr dokładnie , z tym że a szablon-argument wyprowadzona z wiązania tablicy może być dowolnej całki Typ. [ Przykład:

template<int i> class A { / ... / };
template<short s> void f(A<s>);
  void k1() {
  A<1> a;
  f(a); // error: deduction fails for conversion from int to short
  f<1>(a); // OK
}

Wynik jest taki, że częściowa specjalizacja is_int nie może zostać wydedukowana, ponieważ nie przyjmuje dokładnie tego samego typu (unsigned long long vs long long) jako formalny parametr szablonu Int.

Możesz rozwiązać ten problem, podając parametr szablonu non-type N w częściowej specjalizacji is_int ten sam typ, co parametr non-type N w głównym szablonie Int.

template <IntType N>
//        ^^^^^^^^         
struct is_int<Int<N>> : std::true_type {};

Przykład Na Żywo.

 25
Author: TemplateRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-29 07:49:48

Clang jest niekonsekwentny. ponieważ akceptuje Twój kod , oczekuję, że następujący kod musi wyjść f(Int<long long>) zamiast f(T):

using IntType = unsigned long long;
template <IntType N> struct Int {};

template<typename T>
void f(T) { std::cout << "f(T)" << std::endl; }

template<long long N>
void f(Int<N>) { std::cout << "f(Int<long long>)" << std::endl; }

int main()
{
    f(Int<0>{});
}

Ale o dziwo, wychodzi to ( demo online):

f(T)

, które pokazuje Int<0> nie pasuje do drugiego przeciążenia, które przyjmuje argument jako Int<N>. Jeśli tak, to dlaczego pasuje do Int<N>, gdy jest używany jako argument szablonu do szablonu klasy (w Twoim przypadku)?

Mój wniosek:

  • jeśli Clang jest poprawny w moim przypadku , to jest niepoprawny w Twoim przypadku .
  • jeśli Clang jest poprawny w Twojej sprawie, to jest niepoprawny w mojej sprawie.

Tak czy inaczej, Clang wydaje się mieć Buga.

GCC, z drugiej strony, jest co najmniej spójne. To nie dowodzi jednak, że nie ma błędu - może to oznaczać, że ma błąd w obu przypadkach! Chyba, że ktoś wymyśli standard i pokaże ma też bug , zaufam GCC w tym przypadku.

 10
Author: Nawaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-28 14:36:30