std::source_location as non type template parameter

971 views Asked by At

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.

1

There are 1 answers

4
Barry On BEST ANSWER

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.