How to pass a template parameter to an object without calling its member functions?

71 views Asked by At

I can pass a template parameter to an object's member function, but not to the object itself.
Is this possible at all?

#include <iostream>
#include <string>
using namespace std;

class Logger {
public:

    Logger& operator()(int t) {
        cerr << "(tag " << t << ") ";
        return *this;
    }

    Logger& operator<<(const string &s) {
        cerr << s << endl;
        return *this;
    }

    template<int t> Logger& Tag() {
        cerr << "(tag " << t << ") ";
        return *this;
    }

    Logger() {}

    template<int t> Logger() {
        cerr << "(tag " << t << ") ";
    }
};

int main() {
    {
        Logger log;
        log(2) << "World";
        log(3) << "World";
        log.Tag<2>() << "Template 1";
        log<2> << "Template 2";        // <-- error
    }
}

This is the error message (from GCC 13.2):

error: invalid operands to binary expression ('Logger' and 'int')
        log<2> << "Template 2";
        ~~~^~

What I would like to achieve is to have log<2> and log.Tag<2> behave in the same way.

1

There are 1 answers

1
HolyBlackCat On BEST ANSWER

The only way to achieve the log<i> << ... syntax is to make log a variable template. Note that this will break log << ... and log.Foo() syntax.

#include <iostream>
#include <string>

class Logger
{
    int loglevel = 0;
  public:
    Logger(int loglevel) : loglevel(loglevel) {}

    Logger& operator<<(const std::string &s)
    {
        std::cerr << loglevel << "->" << s << '\n';
        return *this;
    }
};
template <int N> Logger log(N);

int main()
{
    log<2> << "blah";
}

This would give you N "loggers", one per log level, so they should probably hold pointers to the one single underlying logger.

I don't think this is a good idea overall. Firstly, too much effort for a tiny bit of syntax sugar. And second, in the modern day you should probably design your logger around std::format, which means all your logging statements become function calls, such as:

log(i, "format", args...);
log<i>("format", args...);

And this call syntax is trivial to implement.