c++templateslanguage-lawyerc++20generic-type-argument

Are virtual Functions allowed for scalar types - used as non-type template argument


I'd like to create compile-time string literals with a custom layout.

Therefore I wrote this code (simplified):

#include <cstdint>
#include <type_traits>
#include <array>

template<
    std::size_t Size,
    typename CharType
>
struct string_literal
{
    std::array<std::remove_const_t<CharType>, Size> chars;

    constexpr string_literal(CharType(&literal_array)[Size]) :
        chars(std::to_array<CharType, Size>(literal_array))
    {
    }

    //causes an error with g++
    virtual void Foo(){}

    constexpr ~string_literal(){}
};

template <string_literal t>
constexpr auto* operator ""_s()
{
    return &std::integral_constant<decltype(t), t>::value;
}

int main(){
    auto x = u"Hello world"_s;
    return 0;
}

It breaks on g++ when I add a virtual function so my question is: is that not allowed or is it g++-bug (it compiles with clang, msvc and intel-compiler)?

The actual error message is:

<source>: In function 'int main()':
<source>:32:14: error: no matching function for call to 'operator""_s<"H\000e\000l\000l\000o\000 \000w\000o\000r\000l\000d\000\000">()'
   32 |     auto x = u"Hello world"_s;
      |              ^~~~~~~~~~~~~~~~
<source>:32:14: note: there is 1 candidate
<source>:26:17: note: candidate 1: 'template<string_literal<...auto...> t> constexpr auto* operator""_s()'
   26 | constexpr auto* operator ""_s()
      |                 ^~~~~~~~
<source>:26:17: note: template argument deduction/substitution failed:
<source>:32:14: error: '((& string_literal<12, const char16_t>::_ZTV14string_literalILm12EKDsE) + 16)' is not a valid template argument for 'int (**)(...)' because it is not the address of a variable
   32 |     auto x = u"Hello world"_s;
      |              ^~~~~~~~~~~~~~~~

Here a more simplified version of the problem:

class Foo{
    virtual void Bar(){
    }
};

template<Foo foo>
void XXX(){

}

int main(){
    constexpr static auto foo = Foo();
    XXX<foo>();
    return 0;
}

Solution

  • clang is correct here. The standard gives no limitation on whether literal types can or cannot have virtual members (basic.types/10.5):

    A type is a literal type if it is:

    ...

    • a possibly cv-qualified class type that has all of the following properties:
      • it has a constexpr destructor,
      • it is either a closure type, an aggregate type, or has at least one constexpr constructor or constructor template (possibly inherited from a base class) that is not a copy or move constructor,
      • ...
      • if it is not a union, all of its non-static data members and base classes are of non-volatile literal types.

    nor it's required for NTTP (temp.param/7.3):

    • A non-type template-parameter shall have one of the following (possibly cv-qualified) types:
      • a structural type (see below), ...

    ...

    • A structural type is one of the following:
      • ...
      • a literal class type with the following properties:
        • all base classes and non-static data members are public and non-mutable and
        • the types of all bases classes and non-static data members are structural types or (possibly multi-dimensional) array thereof.

    Thus I see no reason for GCC to reject that code, as your class meets the given requirements