I am working on a c++ project where I am using the CRTP to provide type information about objects for serialization. I added a static constexpr TypeID TYPE_ID
and set it to Derived::TYPE_ID
and started getting compiler errors about TYPE_ID
not being a member of my derived class TestObject
. I created a minimal version of this setup which worked fine but I realised that I was compiling the minimal version with g++ and the full version with MSVC. Compiling the minimal version with cl.exe gave me the errors again.
Here is my minimal code:
base.h
#ifndef BASE_H
#define BASE_H
#include <string_view>
class IBase {
public:
virtual ~IBase() = default;
};
template<typename Derived>
class Base : public IBase {
public:
static constexpr std::string_view TYPE_ID = Derived::TYPE_ID;
};
#endif
main.cpp
#include <iostream>
#include "base.h"
class Derived : public Base<Derived> {
public:
static constexpr std::string_view TYPE_ID = "Derived";
};
int main() {
std::cout << Base<Derived>::TYPE_ID << std::endl;
}
Compiling with cl.exe gives the following output:
Microsoft (R) C/C++ Optimizing Compiler Version 19.38.33141 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
main.cpp
C:\Users\Harry\temp\cpp_testing\CRTP-test-mimimal\base.h(14): error C2039: 'TYPE_ID': is not a member of 'Derived'
.\main.cpp(5): note: see declaration of 'Derived'
C:\Users\Harry\temp\cpp_testing\CRTP-test-mimimal\base.h(14): note: the template instantiation context (the oldest one first) is
.\main.cpp(5): note: see reference to class template instantiation 'Base<Derived>' being compiled
C:\Users\Harry\temp\cpp_testing\CRTP-test-mimimal\base.h(14): error C2065: 'TYPE_ID': undeclared identifier
C:\Users\Harry\temp\cpp_testing\CRTP-test-mimimal\base.h(14): error C2131: expression did not evaluate to a constant
C:\Users\Harry\temp\cpp_testing\CRTP-test-mimimal\base.h(14): note: a non-constant (sub-)expression was encountered
But when using g++ it compiles with no errors and runs as expected.
Why is this happening and is there any way to get this to work with the MSVC compiler?
As a workaround, you could use a static function instead for TYPE_ID
and use the CRTP by calling the Derived
implementation from the base class inside the body of the TYPE_ID
function:
#include <string_view>
#include <iostream>
class IBase {
public:
virtual ~IBase() = default;
};
template<typename Derived>
class Base : public IBase {
public:
static constexpr std::string_view TYPE_ID() { return Derived::TYPE_ID(); }
};
class Derived : public Base<Derived> {
public:
static constexpr std::string_view TYPE_ID() { return std::string_view {"Derived"}; }
};
int main() {
std::cout << Base<Derived>::TYPE_ID() << std::endl;
}