Template mit strongly typed enum Argumenten/Parametern



  • Hallo Forum, ich falle gleich einmal mit einer Frage ins Haus.

    Ich habe ein kleines Problem beim Benutzen von Templates mit strongly typed enums. Ich versuche grad Code den ich mir clang/gcc erfolgreich kompiliere auf den neuen Visual Studio 2015 Compiler zu übertragen, da dieser nun C++11 usw. besser unterstützen soll. Dabei bin ich auf folgendes Problem gestoßen:

    enum class Foo {
        Enable,
        Disable,
    };
    
    enum class Bar {
        Enable,
        Disable,
    };
    
    11 template<Foo> auto fn() -> void {};
    12 template<Bar> auto fn() -> void {};
    
    14 template<> auto fn<Foo::Enable>() -> void {};
    15 template<> auto fn<Bar::Enable>() -> void {};
    
    1>main.cpp(14): error C2912: explicit specialization 'void fn<Enable>(void)' is not a specialization of a function template
    1>main.cpp(15): error C2912: explicit specialization 'void fn<Enable>(void)' is not a specialization of a function template
    

    Lustiger weise nicht wenn ich ein Template weg lasse, dann kompilieren beide Spezialisierungen.)

    17 int main(int argc, char** argv) {
    18    fn<Bar::Enable>();
    19    fn<Foo::Enable>();
    20    return 0;
    21 }
    
    1>main.cpp(19): error C2668: 'fn': ambiguous call to overloaded function
    1>  main.cpp(12): note: could be 'void fn<Enable>(void)'
    1>  main.cpp(11): note: or       'void fn<Enable>(void)'
    1>  main.cpp(19): note: while trying to match the argument list '()'
    

    Es scheint so als ob der Visual Studio Compiler den zugrundeliegenden Typen des enums benutzt. Hierbei komme ich jetzt ins Schleudern, welches Verhalten der Compiler jetzt das richtige ist. Von meinem Verständnis würde ich jetzt sagen, clang/gcc verhalten sich korrekt, würde mir auch besser passen ;). Ich bin leider nicht fit genug in C++ um jetzt jedoch einschätzen zu können welches das richtige Verhalten ist.

    Da ich noch etwas unsicher mit C++ bin würde ich mich freuen wenn mir jemand sagen könnte ob dies nur doch eine Unzulänglichkeit von Visual Studio ist, oder ich Templates misshandle (oder beides).
    Ich kann gerne auch eine zusammenhängende main.cpp posten, wenn es oben zu zerstückelt ist.

    Schon einmal danke,

    Alex


  • Mod

    VC++ hat scheinbar Recht.

    §14.8.2.6/1 schrieb:

    In a declaration whose declarator-id refers to a specialization of a function template, template argument deduction is performed to identify the specialization to which the declaration refers. Specifically, this is done for […] explicit specializations (14.7.3) […]. In all these cases, P is the type of the function template being considered as a potential match and A is […] the function type from the declaration […]. The deduction is done as described in 14.8.2.5.

    Jedoch ist der Typ jeder Spezialisierung beider Templates void() . Die Deduktion wird also stets erfolgreich sein, und demnach greift der zweite Paragraph:

    If, for the set of function templates so considered, there is either no match or more than one match after partial ordering has been considered (14.5.6.2), deduction fails and, in the declaration cases, the program is ill-formed.

    (Partial ordering wird selbstverständlich keine der beiden Überladungen als spezialisierter betrachten.)

    Ich bin mir aber nicht sicher ob obige Argumentation so tatsächlich stimmt, und würde intuitiv definitiv auf einen Bug tippen. Denn folgendes wäre demnach auch falsch:

    template<int> void fn() {};
    template<typename> void fn() {};
    
    template<> void fn<0>() {};
    

    .. und wird von VC++ problemlos kompiliert. Wahrscheinlich wird irgendwo noch vermerkt dass die Art des Template-Parameters mit der des Arguments übereinstimmen muss (und ich überseh's grad').


  • Mod

    Folgender Abschnitt sollte meine unsinnige Folgerung von oben beheben:

    §14.8.2/2 schrieb:

    When an explicit template argument list is specified, the template arguments must be compatible with the template parameter list and must result in a valid function type as described below; otherwise type deduction fails. Specifically, the following steps are performed when evaluating an explicitly specified template argument list with respect to a given function template:

    (2.1) — The specified template arguments must match the template parameters in kind (i.e., type, non-type, template). There must not be more arguments than there are parameters unless at least one parameter is a template parameter pack, and there shall be an argument for each non-pack parameter. Otherwise, type deduction fails.

    (2.2) — Non-type arguments must match the types of the corresponding non-type template parameters, or must be convertible to the types of the corresponding non-type parameters as specified in 14.3.2, otherwise type deduction fails.

    (2.3) — The specified template argument values are substituted for the corresponding template parameters as specified below.

    I.e. dein Code ist in Ordnung, und VC++ ignoriert das zweite Item dieser Liste - die Typen werden nicht entsprechend verglichen. Beachtet man

    template <int> void f();
    template <void*> void f();
    
    template<> void f<1>() {}
    

    Wird das umso deutlicher: VC++ gibt die selbe Fehlermeldung aus.



  • Wow, erst einmal vielen lieben Dank, dass du so tief in den Standard abgetaucht bist um mir zu antworten. Ich frage mich immer wie man so etwas alles wissen soll. Dann weiß ich ja jetzt wo ich einen Bugreport eröffnen kann.

    Ich bin ehrlich gesagt froh, dass sich die strongly typed enums so wie ich es erwarten würde verhalten sollen, bin da mit den typedefs schon auf die Nase gefallen. Aufgrund der typedefs bin ich für mich auch ins Schleudern gekommen, ob Erwartung der richtige Anhaltspunkt für das korrekte Verhalten ist.

    So viel zu lernen, so viel. Vielen Dank noch einmal, du hast mir sehr geholfen.


Log in to reply