2

Q: Is this a strictly conforming C++17 program where the return value isn't (shouldn't be) implementation defined, and if so, what is the correct return value?

template <char... Chars>
struct foo { inline static constexpr bool primary = true; };

template <bool Bool, char... Chars>
struct foo<Bool, Chars...> { inline static constexpr bool primary = false; }; // 5

int main() {
    return foo<true>::primary;       // gcc: 1, clang & icx: 0
}

My expectation was for the specialization to be selected (by all compilers) because foo<true> matches the specialization without converting the boolean true to char and also because the specialization is more constrained than the primary template (since it requires at least one parameter). Unfortunately, we use a gcc 7.3 -based toolchain where the above doesn't even compile, but the output from gcc 7.3 may provide a hint to someone answering this question:

<source>:5:8: error: template argument '(char)Bool' involves template parameter(s)
 struct foo<Bool, Chars...> { inline static constexpr bool primary = false; };
        ^~~~~~~~~~~~~~~~~~~

I therefore tried it out with gcc (trunk) in Compiler Explorer but to my surprise, even though gcc (trunk) compiles it, it selects the primary template.

If I change true to a 'a', clang & icx also selects the primary template, just like I want.

template <char... Chars>
struct foo { inline static constexpr bool primary = true; };

template <bool Bool, char... Chars>
struct foo<Bool, Chars...> { inline static constexpr bool primary = false; };

int main() {
    return foo<'a'>::primary;       // gcc: 1, clang & icx: 1
}

I've tried to find the answer in [temp] in the final C++17 draft but I'm not able to piece it together.


Fwiw, even though the question was closed as a duplicate, I'll document my workaround here.

I made a detail::foo_impl which will always be used as a base class:

#include <type_traits>

namespace detail {

template <bool Bool, char... Chars>
struct foo_impl {
    static constexpr bool value = Bool;
};

}  // namespace detail

Then the primary became

// The primary template which selects a value for the bool using
// some disjunction or conjunction. Here simplified to a fold:
template <char... Chars>
struct foo : detail::foo_impl<(... || (Chars == ' ')), Chars...> {};

and the specializations will be straight forward:

// Specializations for `false` and `true`:
template <char... Chars>
struct foo<false, Chars...> : detail::foo_impl<false, Chars...> {};

template <char... Chars>
struct foo<true, Chars...> : detail::foo_impl<true, Chars...> {};

Now all compilers agree (even gcc 7.3) ...and also do what I wanted from the start.

Demo

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • About the duplicate: In my code, it's not a matter of narrowing to match the specialization. In my case, it rather a "widening" of the `bool` to match the primary. Are those cases really the same? – Ted Lyngmo Apr 20 '23 at 11:22
  • 1
    It looks like that the accepted answer in the duplicate has a [link](https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1647) which is about the issue you described. However I believe that those two questions should not be considered to be duplicates, even if the answer to both would be about the same issue. – Karen Baghdasaryan Apr 20 '23 at 11:45
  • A template specialization, by definition, is some "subset" of the template. The template parameters are `char`s. A `bool` is not a `char`. This is not a specialization, but a different template that has nothing to do, whatsoever, with the other one. – Sam Varshavchik Apr 20 '23 at 11:47
  • 1
    @KarenBaghdasaryan Thanks! Yes, that link has an example that is _pretty_ close to my case. That specialization has `int, char...` while mine has `bool, char...`. In the link, the `1` would have to be narrowed to `char` while in my example, `true` would have to be widened to `char`. Perhaps not an important distinction. – Ted Lyngmo Apr 20 '23 at 12:17
  • @SamVarshavchik Oh... perhaps there's something in that that can explain what behavior to expect? In the WG21 [link](https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1647) Karen shared they have `template struct char_values` vs. `template struct char_values` and then it's a partial specialization - but if mine is not a specialization, perhaps I need to search for answers in some other section of the standard (or a duplicate Q&A here - but I couldn't find one)? – Ted Lyngmo Apr 20 '23 at 12:23

0 Answers0