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.)
I have a C++ class using Core Audio structs on OS X.
My initial implementation was like this:
class MyClass
{
private:
AUNode _converterNode;
AURenderCallbackStruct _renderCBStruct;
public:
MyClass();
~MyClass();
inline AUNode* getConverterNode() { return &_converterNode; }
inline AURenderCallbackStruct* AURenderCallbackStruct() { return &_renderCBStruct; }
};
After reading the Poco style guides, I wanted to change the order of the private/public blocks. It then looked like this:
class MyClass
{
public:
MyClass();
~MyClass();
inline AUNode* getConverterNode() { return &_converterNode; }
inline AURenderCallbackStruct* AURenderCallbackStruct() { return &_renderCBStruct; }
private:
AUNode _converterNode;
AURenderCallbackStruct _renderCBStruct;
};
The compiler now tells me that the type AURenderCallbackStruct is unknown and tells me to replace the type name with ::AURenderCallbackStruct. When I do that, there are no compiler errors.
Weirdly, this only appears for the `AURenderCallbackStruct and not the AUNode.
The AURenderCallbackStruct is defined like this:
typedef struct AURenderCallbackStruct {
AURenderCallback inputProc;
void * inputProcRefCon;
} AURenderCallbackStruct;
and AUNode is defined like this:
typedef SInt32 AUNode;
Can anyone explain why the change of order of private/public block produces a compiler error and why the error disappears when adding a ::in front of the type?
First of all it is not clear why you named the member function as AURenderCallbackStruct that coincides with the corresponding structure's name and looks like the structure constructor.
The problem is this stupid name.
in the first case the compiler thinks that you indeed define the member function that hides the corresponding name of the structure.
In the second case the compiler thinks that you trying to call the constructor of the structure.
Simply rename the function that there would not be an ambiguity.
I have a static member of a class of type map. but whenever I try to access it I always get an error. For example
// a.h
class a {
public:
static map<string, int>m;
a() {
}
~a() {
}
};
// a.cpp
a::m['ADD']=1;
this is the error I get = "Size of array has non-integer type 'const char[4]"
I also get a weird error in the linker.
You need to define the map before you can use it:
std::map<std::string, int> a::m = std::map<std::string, int>{{"ADD", 1}};
This will initialize it to a map holding one element with key std::string("ADD") and value 1.
Note also, use double quotes for string literals.
Compiler error: You are using single-quotes when you probably meant to use double quotes.
Linker error: When defining static variables of a class, you also have to define them in a cpp file somewhere. Add the following line to your cpp file:
map<string, int> a::m;
I have a class called AppSettings where I have an Array with a range of note frequencies. I'm getting several errors with the code below and I'm not sure what the problem is.
The error messages are:
static data member of type 'const float [36] must be initialized out of line
A brace enclosed initializer is not allowed here before '{' token
Invalid in-class initialization of static data member of non-integral type
And the code:
class AppSettings{
public:
static const float noteFrequency[36] = {
// C C# D D# E F F# G G# A A# B
130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 223.08, 246.94,
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77
};
};
As the name suggests this is just a header file with some settings and values I need throughout the app.
You can't define the value of static class members within the class. You need to have a line like this in the class:
class AppSettings
{
public:
static const float noteFrequency[];
And then in an implementation file for the class (AppSettings.cpp perhaps):
const float AppSettings::noteFrequency[] = { /* ... */ };
Also, you don't need to specify the number within the [] here, because C++ is smart enough to count the number of elements in your initialization value.
This works just fine in C++11
class AppSettings{
public:
static constexpr float noteFrequency[36] = {
// C C# D D# E F F# G G# A A# B
130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 223.08, 246.94,
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77
};
};
C++03 doesn't support in-class definitions of complex data like arrays of constants.
To place such a definition at namespace scope in a header file, and avoid breaking the One Definition Rule, you can leverage a special exemption for template classes, as follows:
#include <iostream>
using namespace std;
//----------------------------------------- BEGIN header file region
template< class Dummy >
struct Frequencies_
{
static const double noteFrequency[36];
};
template< class Dummy >
double const Frequencies_<Dummy>::noteFrequency[36] =
{
// C C# D D# E F F# G G# A A# B
130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 223.08, 246.94,
261.63, 277.18, 293.66, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88,
523.25, 554.37, 587.33, 622.25, 659.25, 698.46, 739.99, 783.99, 830.61, 880.00, 932.33, 987.77
};
class AppSettings
: public Frequencies_<void>
{
public:
};
//----------------------------------------- END header file region
int main()
{
double const a = AppSettings::noteFrequency[21];
wcout << a << endl;
}
There are also some other techniques that can be used:
An inline function producing a reference to the array (or used as indexer).
Placing the definition in a separately compiled file.
Simply computing the numbers as needed.
Without more information I wouldn’t want to make the choice for you, but it shouldn’t be a difficult choice.
When I write code like this in VS 2008:
.h
struct Patterns {
string ptCreate;
string ptDelete;
string ptDrop;
string ptUpdate;
string ptInsert;
string ptSelect;
};
class QueryValidate {
string query;
string pattern;
static Patterns pts;
public:
friend class Query;
QueryValidate(const string& qr, const string& ptn):
query(qr), pattern(ptn) {}
bool validate() {
boost::regex rg(pattern);
return boost::regex_match(query, rg);
}
virtual ~QueryValidate() {}
};
I then initialize my structure like this:
.cpp
string QueryValidate::pts::ptCreate = "something";
string QueryValidate::pts::ptDelete = "something";
//...
The compiler gives the following errors:
'Patterns': the symbol to the left of a '::' must be a type 'ptSelect'
: is not a member of 'QueryValidate'
What am I doing wrong? Is this a problem with Visual Studio or with my code? I know that static members except for const ones must be defined outside the class they were declared in.
You're trying to create a non-static member (ptCreate) of a static member (pts). This won't work like this.
You got two options, either use a struct initializer list for the Patterns class.
Patterns QueryValidate::pts = {"CREATE", "DELETE"}; // etc. for every string
Or, much safer (and better in my opinion), provide a constructor in Patterns and call that one.
struct Patterns {
Patterns() { /*...*/ }
/* ... */
}
On a side not, your code wouldn't work in any C++ compiler, it's not a conflict with Visual Studio things.
You can only initialize the structure as a whole, as in:
Patterns QueryValidate::pts = { "something", "something", ... };
This isn't valid C++. In the cpp file you're declaring parts of the static structure "QueryValidate::pts", but that's not allowed: you've got to declare the whole structure, like so:
Patterns QueryValidate::pts;
if you want members to be initialized, you either initialize them in another method, or add a constructor to Patterns that takes whatever initialization arguments you want.
I'm not real sure what you are trying to do here. It looks kind of like you are trying to declare and initialize each field in pts separately, rather than declare pts once as a single object. I'm really surprised VS lets you do that.
What worked for me in gcc was the following:
Patterns QueryValidate::pts;
void foo () {
QueryValidate::pts.ptCreate = "something";
QueryValidate::pts.ptDelete = "something";
}