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.