2

First of all, I know this question might seem a duplicate. But I've read many posts with a similar question and didn't find an answer.

Second of all, I haven't had any issue with my solution so far. So I'm asking rather if my solution is a correct one with no pitfalls.

Suppose I have a class template Particle which is supposed to be a base class in the CRTP and is intended to have a static variable fName which may differ from instance to instance:

Base

Particle.h

#pragma once

#include <iostream>
#include <string>

template<typename CONCRETE_PARTICLE>
class Particle
{
    private :
        static const std::string fName;

    public :
        static const std::string& GetName() { return fName; }
};

// Default name. If I'm not mistaken this is fine regarding ODR.
template<typename CONCRETE_PARTICLE>
const std::string Particle<CONCRETE_PARTICLE>::fName = "Unknown";

So other particles inherit (and instantiate) from this class. Suppose the user creates a new Particle and they should have a choice either to specify a name of their new particle or not. In the latter case the name would be "Unknown".

Unnamed particle

Tachyon.h

#pragma once

#include "Particle.h"

class Tachyon : public Particle<Tachyon>
{
    public :
        Tachyon();
        ~Tachyon() = default;
};

with nothing special in the Tachyon.cpp (I made .cpp files in order to check that ODR isn't violated).

On the other hand, what if the name is to be specified. What should they do?

Named particle

Muon.h

#pragma once

#include "Particle.h"

class Muon : public Particle<Muon>
{
    public :
        Muon();
        ~Muon() = default;

};

// Specification declaration (???)
// Tell the compiler that Particle<Muon>::fName is defined somewhere
// and not to take the default value into account (???)
template<>
const std::string Particle<Muon>::fName;

Muon.cpp

#include "Muon.h"

Muon::Muon() {}

template<>
const std::string Particle<Muon>::fName = "Muon";

Main.cpp

#include "Muon.h"
#include "Tachyon.h"

int main()
{
    std::cout << Particle<Muon>::GetName() << "\n"; // prints "Muon"
    std::cout << Particle<Tachyon>::GetName() << "\n"; // prints "Unknown"
    return 0;
}

As I said I don't get any error and the program works as intended. But I am a bit confused by the mix of CRTP + ODR + static data member declaration/definition so the question.

AVH
  • 11,349
  • 4
  • 34
  • 43
LRDPRDX
  • 631
  • 1
  • 11
  • 23
  • I’m voting to close this question because this should be asked on https://softwareengineering.stackexchange.com/. – cse Oct 09 '21 at 20:13
  • You might want to remove those defaulted destructors, since they disable compiler-generated move constructor/assignment: https://stackoverflow.com/questions/56968443/explicitly-defaulted-destructor-disables-default-move-constructor-in-a-class. – AVH Oct 09 '21 at 20:16
  • @AVH, Thank you. Will take into account. – LRDPRDX Oct 09 '21 at 20:17
  • @cse I don't see how this pertains to software engineering. As far as I can see this is strictly a C++ question related to ODR. The title is kind of different from the question though, so I've edited it. – AVH Oct 09 '21 at 20:19
  • @cse I don't know whether this question would be suitable on Software Engineering, or not, but it's definitely on-topic on this site. Even if it's suitable on another site, that's not a reason to close a question. – cigien Oct 09 '21 at 20:25
  • By the way, for what it's worth, I think this code is fine and doesn't violate ODR. But since I'm not 100% sure, I'll leave it to the experts to answer. See e.g. https://stackoverflow.com/questions/54268509/template-specialization-and-static-members-initialization. – AVH Oct 09 '21 at 20:27
  • @LRDPRDX I find it suitable under tag [code-quality] + [code-reviews] on https://softwareengineering.stackexchange.com/. But no other members here think that this question is off-topic so it should be fine here. – cse Oct 09 '21 at 20:33
  • @cse, I think the Software Engineering SE is more about the design. I'm not asking about the design (though it is relevant). I would say my question is more for the Code Review SE (and I initially wanted to ask it there). BUT. I think the code should work for the CR SE which I'm not sure it does :) I can understand your reasoning bc usually questions on SO contain *actual* issues of the posted code not *potential*. This is my point of view. – LRDPRDX Oct 09 '21 at 21:12

1 Answers1

1

It’s not clear exactly what rule you think this might violate, but this program is well formed. In particular, explicit specializations of a templated static variable are definitions only if they have an initializer ([temp.expl.spec]/14). (Non-member variable templates instead use extern to merely declare specializations.) Nor is there any use of the primary template before the explicit specialization is declared so long as the derived class doesn’t do so.

That said, you could easily avoid using specializations here entirely: use SFINAE to detect a member fName and return"Unknown" if there’s none. Or just look up fName as CONCRETE_PARTICLE::fName to use any declared there. (Incidentally, don’t use MACRO_NAMES for template parameters.)

Davis Herring
  • 36,443
  • 4
  • 48
  • 76