c++c++20std-source-location

std::source_location as non type template parameter


In my infinite quest to push limits of what can be used as non type template parameter I was trying to see if I can use std::source_location as non type template parameter. That failed with a weird message, since I presume source_location is some magical struct...

type 'std::experimental::source_location' of non-type template parameter is not a structural type

It failed, so I tried to workaround that with using .file_name, but that also fails (godbolt).

note: candidate template ignored: substitution failure: pointer to subobject of string literal is not allowed in a template argument

#include<iostream>
#include<experimental/source_location>

template<auto src_loc = std::experimental::source_location::current().file_name()>
void log_first(){
    static bool dummy =([]{
        std::cout << "Logging first call" + src_loc << std::endl;
    }(), false);
}

int main() {
    log_first();
    log_first();
}

Is there any way to make this work without use of macros?

To be clear I am asking about using source_location as template parameter, not about solving my toy example, it is just here to demonstrate potential use case.


Solution

  • std::source_location is specified as:

      struct source_location {
        // ...
    
      private:
        uint_least32_t line_;               // exposition only
        uint_least32_t column_;             // exposition only
        const char* file_name_;             // exposition only
        const char* function_name_;         // exposition only
      };
    

    And the rules for the kinds of types that can be used as non-template template parameters require that a type be structural, which means, from [temp.param]/7, emphasis mine:

    A structural type is one of the following:

    • a scalar type, or
    • an lvalue reference type, or
    • 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.

    source_location does not have all of its non-static data members public, so it is not structural, so it is not usable as a non-type template parameter.


    This part:

    template <auto src_loc = std::experimental::source_location::current().file_name()>
    

    does not work because of [temp.arg.nontype]/3:

    For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

    • [...],
    • a string literal object ([lex.string]),
    • ...

    But what you can do is create your own type, that is structural, that is constructible from source_location. It's just that the strings can't be char const*, they have to own the data. If you look in the examples in P0732, we can build up:

    template <typename Char, size_t N>
    struct basic_fixed_string { ... };
    
    template <basic_fixed_string S> struct A {};
    
    using T = A<"hello">;
    

    That might be awkward to deal with in this case, so you could also just pick some reasonable max size and go with that.