c++templatesvisual-c++g++crtp

Why does accessing a static member of the derived type in CRTP work with g++ but not cl.exe?


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?


Solution

  • 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;
    }
    

    Demo