Does static data member specialization in CRTP violate ODR? - c++

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.

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.)

Related

Forward Declarations of recursive templates using Boost::Units

as i am seeking to reduce the compile times of our code, i am currently trying to reduce heavy includes in header files. For this, i am forward declaring function parameters as in this example:
// Class A.h
class B;
class A
{
...
void foo(B);
}
However, i did not find a way to forward declare our typedefinitions, that rely on boost units such as the definition of a length unit (Length.h) required as a function parameter in the file Object.h:
// Length.h
using meter_unit = boost::units::si::meter_base_unit::unit_type;
using Length = boost::units::quantity<meter_unit, double>;
BOOST_UNITS_STATIC_CONSTANT(Meter, meter_unit);
BOOST_UNITS_STATIC_CONSTANT(Meters, meter_unit)
// Object.h
#include <Length.h> // This include shall be avoided
class Position;
class Object {
...
bool isNearby(Position pos, Length distance);
}
Are there any suggestions on how i could achieve this?
What I Tried:
First Approach: I tried forward declaring the template boost::units::quantity<meter_unit, double>;
but i struggeld to define the meter_unit that contains both template class meter_base_unit and unit_type inside the namespace of meter_unit.
The meter_base_unit is defined inside boost units as below (simplified) and i guess the unit_type is defined inside the macro BOOST_TYPEOF_REGISTER_TYPE.
// Boost ... Units/Meter.hpp
namespace boost::units::si {
struct meter_base_unit : public base_unit<meter_base_unit, length_dimension, -9>
{
static std::string name() { return("meter"); }
static std::string symbol() { return("m"); }
};
}
}
BOOST_TYPEOF_REGISTER_TYPE(boost::units::si::meter_base_unit)
Is such a constellation even possible to forward declare? And if not, do alternatives exist that could have the same benefit (avoided include while still using the Boost Units Library).
Second Approach: Defining classes that inherit from the respective units such as:
class Length : public boost::units::quantity<meter_unit, double>
but the problem is, that i then have to create CTR for every possible unit that i try to initialize the Length unit with (Feet, Meter, Kilometer a.s.o), which is basically re-implementing the library.
Third Approach: Creating a class that contains only the length unit as a variable, which then leads to overloading all possible operators for that class.
I am happy for every contribution regarding my specific problem and every contribution leading to deeper understanding of template forward declarations.
Thanks in Advance
SegfaultCreator

Learning C++ and having a problem correclty separating class interface from implementation

I'm learning C++ using Xcode and have written several small programs including a hangman game but I'm having trouble every time I try to separate a class into definition and implementation. I made a simple case that shows my problem. Short version is it seems that I need to specify a type in the implementation file even though it is already defined in the header file. I get "C++ requires a type specifier for all declarations" on lines 12 and 13 in my example. But if I change line 12, for example, to
int xmlelem::atrb_count = 0;
it gets the error "non-static data member defined out-of-line". In other cases I have got an error saying that I was trying to redefine something. I think I'm missing a fundamental concept somewhere. I did not see this particular issue in the handful of similar questions I looked at.
xmlelem.hpp
// xmlelem.hpp
// learn header
//
//
#ifndef xmlelem_hpp
#define xmlelem_hpp
#include <stdio.h>
#include <string>
#endif /* xmlelem_hpp */
class xmlelem {
private:
int atrb_count;
std::string tag_name;
public:
xmlelem(std::string tag);
void add_atrib();
std::string output();
};
xmlelem.cpp
// xmlelem.cpp
// learn header
//.
//
#include "xmlelem.hpp"
#include "string"
#include <iostream>
// line 11
xmlelem::atrb_count = 0;
xmlelem::tag_name = "";
xmlelem::xmlelem(std::string tag){
tag_name = tag;
}
void xmlelem::add_atrib(){
atrb_count++;
}
std::string xmlelem::output(){
std::string build = "<";
build = build + tag_name + " " + std::to_string(atrb_count);
build = build + ">";
return build;
}
and main.cpp
// main.cpp
// learn header
//
//
#include <iostream>
#include "xmlelem.hpp"
using namespace std;
int main(){
xmlelem clip("test)");
std::cout << clip.output() << " test \n";
}
Let's take a look at the (second) error message.
non-static data member defined out-of-line
There are two parts to the error: "non-static data member" and "defined out-of-line". These are incompatible, so one of them must be changed. Furthermore, only one of them should be changed, or else you may run into a different problem. Decide which of the two parts is correct for your situation.
Keep "defined out-of-line"
When the line
int xmlelem::atrb_count = 0;
is encountered at namespace scope (that is, in neither a function nor a class/struct/union definition), it is an out-of-line definition. This definition tells the compiler to reserve, right at that spot, enough space for an int. Then whenever any xmlelem object accesses the atrb_count member, it will access this particular space. So there is one int shared by all objects.
However, this behavior corresponds to a static member. To make the declaration agree with the implementation, the keyword static needs to be added.
class xmlelem {
private:
static int atrb_count;
/* rest of the class definition */
};
Keep "non-static"
A non-static data member is stored inside each object of the class. Each object can do what it wants with its copy of the data without impacting other objects. So telling the compiler to reserve space outside the objects is contradictory. Simply removing the out-of-line definition is enough to get rid of the error message, but presumably you wanted that initialization to occur somewhere, right?
The initialization of non-static data members can be done either in-line or in a constructor. An example of moving the initialization in-line is the following.
class xmlelem {
private:
int atrb_count = 0;
/* rest of the class definition */
};
This is sometimes reasonable, but the stated goal was to separate the interface from the implementation. Therefore, it might be undesirable for the initial value of 0 to appear in the header file, as it does in the above. The alternative is to move the initial value to the constructor (to each constructor, if you had more than one).
xmlelem::xmlelem(std::string tag) :
atrb_count(0),
tag_name(tag)
{
}
(I've also taken the liberty of moving the initialization of tag_name into the initialization list.)
Remember, if you have more than one constructor, this needs to be done in each constructor that actually utilizes the default value (for an exception, think "copy constructor"). Repeated code is a drawback; it is up to you to decide if the gains are worth the cost.
Remember that you are declaring a class. A class is an abstract concept. When you do this xlemem::atrb_count = 0;, you are having a concrete value on an abstract concept. Doesn't make sense, right? You don't think of a particular color when you think of the general concept of dog. Any initiliazations should be done inside the constructor, because only in the constructor is that we create a concrete object.
Therefore, you should eliminate lines 11 and 12 where you initialize these 2 attributes and your constructor code should be changed to:
xmlelem::xmlelem(std::string tag){
tag_name = tag;
atrb_count = 0;
}
Note that it isn't necessary to initialize a string to "".

Is there a way to avoid constexpr function used in header file from entering global scope without extra namespace for it?

I have a header file with code simply looking like this:
constexpr uint32 GenTag(const char tag[5]) { ... }
class SomeClass
{
static constexpr uint32 TAG1 = GenTag("smth");
static constexpr uint32 TAG2 = GenTag("abcd");
};
//constexpr needed for switch-case statement
The problem is that function GenTag() belongs to global scope and I would like to avoid it if possible.
I wanted to declare it inside class but it is not possible with constexpr (explanation here: constexpr not working if the function is declared inside class scope).
Does c++ have anything like "undeclare" function at the end of the header (maybe some macro tricks)? Or any other options I missed? If no better ways exist I would probably go with extra (maybe excessive) namespace, but want to ask if there are any other ideas.
There's no way to "undeclare" a function or variable in C++ (header file or not -- a header file is just included into the current translation unit). You'll need to use a namespace, or make GenTag into a macro. You can undefine macros with #undef MACRONAME.
What about defining GenTag() as a static constexpr method in an inherited struct?
If you want that GenTag() is usable only inside SomeClass, you can make it private and define SomeClass as friend inside the class containing GenTag().
I mean... something as follows
#include <iostream>
struct SomeClass;
class foo
{
static constexpr char GenTag(const char tag[5])
{ return tag[0]; }
friend SomeClass;
};
struct SomeClass : public foo
{
static constexpr char TAG1 = GenTag("smth");
static constexpr char TAG2 = GenTag("abcd");
};
int main()
{
std::cout << "Tag1: " << SomeClass::TAG1 << std::endl;
std::cout << "Tag2: " << SomeClass::TAG2 << std::endl;
// compilation error: 'GenTag' is a private member of 'foo'
// static constexpr char TAG3 = foo::GenTag("wxyz");
}
With the friend trick, you don't need to inherit from foo; but, without inheritance, you have to use it as foo::GetTag().
If anyone ever will also meet a problem like this I would recommend you to not use such constexpr in header at all but consider to declare-define it directly in and only in cpp file instead. Then you will definitely avoid unnecessary global scope pollution.
Or if there are multiple places where such constants can be used then make a separate heared for them without any classes and include that file separately from including class definition header.

Syntax error when initializing a static variable

There are two classes defined..
class Dictionary
{
public:
Dictionary();
Dictionary(int i);
// ...
};
and
class Equation
{
static Dictionary operator_list(1);
// ...
};
but the problem is, whenever I compile this, I get a weird error message
error C2059: syntax error : 'constant'
But it compiles well when I use the default constructor on operator_list.
In C++ you cannot combine declaration and initialization. When you do not specify constructor parameters of operator_list, you do not call its default constructor: you simply declare it. You need to also initialize it in the corresponding C++ file, like this:
Equation.h
class Equation {
static Dictionary operator_list;
};
Equation.cpp:
Dictionary Equation::operator_list(1);
Note the absence of static in the CPP file: it is not there by design. The compiler already knows from the declaration that operator_list is static.
Edit: You have a choice with static constant members of integral and enumerated types: you can initialize them in the CPP file as in the example above, or you can give them a value in the header. You still need to define that member in your C++ file, but you must not give it a value at the definition time.
static Dictionary operator_list(); is a function signature declaring a function returning a Dictionary and taking no arguments, that's why your compiler let you do it.
The reasons static Dictionary operator_list(1); fails is because you can't set a value of an complex type in the declaration of your classes. You need to do this elsewhere (e.g. in the .cpp )
For more information, see this post : https://stackoverflow.com/a/3792427/103916
#include <iostream>
using namespace std;
class Dictionary
{
public:
Dictionary() {}
Dictionary(int i):page(i) {}
void display() { cout << "page is " << page << endl; }
private:
int page;
};
class Equation
{
public:
static Dictionary operator_list;
};
Dictionary Equation::operator_list(1); // static members must be initialized this way...
int main()
{
Equation::operator_list.display();
}
Output is:
page is 1

Is there a better way to bring names into class scope other than to typedef them?

I keep running into this issue:
class CCreateShortcutTask : public CTask
{
public:
CCreateShortcutTask(
CFilename filename, // shortcut to create (or overwrite)
Toolbox::Windows::CShellLinkInfo definition // shortcut definition
)
...
Having to spell out Toolbox::Windows::CShellLinkInfo seems highly questionable to me. This is in a header, so I surely don't wish to issue a using namespace declaration or I'll drag that namespace into every client file that includes this header.
But I don't seriously want to have to fully name these things in the class interface itself. I'd really love to be able to do something like:
class CCreateShortcutTask : public CTask
{
using namespace Toolbox::Windows;
-or possibly-
using Toolbox::Windows::CShellLinkInfo;
public:
CCreateShortcutTask(
CFilename filename, // shortcut to create (or overwrite)
CShellLinkInfo definition // shortcut definition
)
...
But these seem to be illegal constructs.
Any ideas how to accomplish this?
EDIT: I have asked a broader version of this question here
I think there is a confusion on typedefs.
Using private typedefs is perfectly suitable (and often used). This relies on the fact that in C++ a typedef does not introduce a new type, but a synonym!
namespace VeryLongNamespaceYoullNeverWantToSeeAgain
{
class SuchAStupidNameShouldBeBanned
{
public:
typedef xxx iterator;
typedef zzz value_type;
static std::string Print(value_type);
private:
};
void destroy(SuchAStupidNameShouldBeBanned&);
}
namespace short
{
class MyStruct
{
typedef VeryLongNamespaceYoullNeverWantToSeeAgain::SuchAStupidNameShouldBeBanned Stupid;
public:
void destroyStupid()
{
Stupid::iterator it = m_stupid.begin(), end = m_stupid.end();
std::cout << "Getting rid of ";
for (; it != end; ++it) std::cout << Stupid::Print(*it) << " ";
std::cout << std::endl;
destroy(m_stupid);
}
private:
Stupid m_stupid;
};
}
Thus, private typedefs don't bother:
name lookup: destroy is correctly invoked
use of inner typedefs: iterator is found
use of static methods: Print is found
So you can really use them without many fuss. Furthermore they don't pollute the global space since they are inner to the class and though visible cannot be manipulated by the client directly so he does not rely on them.
Within the source file, you can use namespace aliasing and using directives (not using namespaces please) more liberally, so it's less of an issue.
Maybe namespace alias will help you in some way. You could write the following:
namespace TW = Toolbox::Windows; // define short name
class CCreateShortcutTask : public CTask
{
public:
CCreateShortcutTask(
CFilename filename,
TW::CShellLinkInfo definition
)