0

I'm trying to create a reference variable cParamIDsByAge to one side of a boost::bimap for convenience (MVCE):

Attempt #1

code.h

#include <boost/bimap.hpp>

typedef boost::bimap <int, int> ParamIDs;
extern const ParamIDs cParamIDs;
extern auto &cParamIDsByAge = cParamIDs.left;

code.cpp

#include "code.h"

// In my code this inits the variable with some data via a function.
const ParamIDs cParamIDs {};
auto &cParamIDsByAge = cParamIDs.left;

The compiler complains:

code.h:5:14: warning: ‘cParamIDsByAge’ initialized and declared ‘extern’
extern auto &cParamIDsByAge = cParamIDs.left;
            ^~~~~~~~~~~~~~
code.cpp:4:7: error: conflicting declaration ‘auto& cParamIDsByAge’
auto &cParamIDsByAge = cParamIDs.left;
    ^~~~~~~~~~~~~~
In file included from code.cpp:1:0:
code.h:5:14: note: previous declaration as ‘const boost::bimaps::views::map_view<boost::bimaps::relation::member_at::left, boost::bimaps::detail::bimap_core<int, int, mpl_::na, mpl_::na, mpl_::na> >& cParamIDsByAge’
extern auto &cParamIDsByAge = cParamIDs.left;
            ^~~~~~~~~~~~~~

Attempt #2

Then I tried defining the reference cParamIDsByAge in the header directly:

code.h

#include <boost/bimap.hpp>

typedef boost::bimap <int, int> ParamIDs;
extern const ParamIDs cParamIDs;
auto &cParamIDsByAge = cParamIDs.left;

code.cpp

#include "code.h"

const ParamIDs cParamIDs {};

But that complained about:

error: multiple definitions of cParamIDsByAge

Attempt #3

I don't initialized the reference in the header:

code.h

#include <boost/bimap.hpp>

typedef boost::bimap <int, int> ParamIDs;
extern const ParamIDs cParamIDs;
extern const auto &cParamIDsByAge;

code.cpp

#include "code.h"
const ParamIDs cParamIDs {};
auto &cParamIDsByAge = cParamIDs.left;

Compiler complains about header file:

error: declaration of ‘const auto& cParamIDsByAge’ has no initializer
DBedrenko
  • 4,871
  • 4
  • 38
  • 73
  • You can't initialize a variable twice. (It follows that you can't use `auto` in the header) – M.M Jun 13 '16 at 08:25
  • @M.M OK, but a reference var must be initialized. The best way I can think of is what I did in Attempt #2, and that doesn't work. – DBedrenko Jun 13 '16 at 08:29

3 Answers3

4

The error message code.h:5:14: warning: ‘cParamIDsByAge’ initialized and declared ‘extern’ is pretty straightforward: in C++, variables declared as extern may not have an initializer. (In other words, extern may only appear in declarations, not definitions).

Sample usage might be:

extern int &foo;

// ... cpp file

int bar;
int &foo = bar;

To do this in your code, there is another issue in that you need to replace int with the type of boost::bimap::left. You can't use auto in the extern declaration because there is no initializer to deduce from.

boost::bimap happens to define this as a typedef so the solution is straightforward:

extern ParamIDs::left_map const &x;

// ... in cpp

const ParamIDs c{};
ParamIDs::left_map const &x = c.left;

If that typedef didn't exist then you could still do it using declval and decltype.

Note 1: I think the standard is unclear on whether auto const &x = c.left; should work here, but gcc and clang both reject it.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • That compiles, thank you! I understand what the problems were now. PS: I read that references are implicitely `const`, so there is no need for the `const` keyword. – DBedrenko Jun 13 '16 at 08:49
  • references are not implicitly const, you probably read someone who was mixed up about the difference between references and pointers – M.M Jun 13 '16 at 09:51
  • It's not the reference that is const, it's the object that it references. The reference itself cannot be modified once initialized, so in that sense yes, it is implicitly const. To declare a reference constant explicitly, you would have to write something like `int bar; int & const foo=bar;`, but this code is ill-formed and there is no associated behaviour. See also [this answer](https://stackoverflow.com/a/52144646/4944216) elsewhere. – il--ya Nov 28 '18 at 19:36
  • @il--ya `const` is a keyword, things are only `const` if they are qualified as such. It doesn't automatically mean anything that's not modifiable. Also, non-const references can be modified (this means modifying the object that the reference refers to). I think you mean to say that references cannot be rebound to different objects once they have been initialized but this is nothing to do with `const` or constantness. – M.M Nov 29 '18 at 02:22
  • Under the hood reference is effectively treated as a dereferenced const pointer, and "bounding" is effectively assigning an initial value to this pointer. It actually can be modified (thus rebound), using some implementation-specific code. " Also, non-const references can be modified (this means modifying the object that the reference refers to)." - but that's two different things, modifying a reference and modifying an object it refers to, don't you think so? – il--ya Nov 29 '18 at 11:38
  • @il--ya i don't follow this "references are const pointers" thing, the standard doesn't support that interpretation and it leads to misconceptions like describing references as const when , according to the standard, they are not – M.M Nov 29 '18 at 19:32
2

Change the line in the header to the following:

// REMOVE: extern auto &cParamIDsByAge = cParamIDs.left;
extern const decltype(cParamIDs.left) &cParamIDsByAge;

This is forward declaring the reference variable properly. The rest should work as is.

eyeApps LLC
  • 643
  • 4
  • 10
  • need a const qualifier – Leon Jun 13 '16 at 08:45
  • If it included `const`, then it would include `&` too. You can make it include both `const` and `&` by adding a couple of extra parentheses: `decltype((cParamIDs.left))` – Leon Jun 13 '16 at 08:49
  • Check it online: https://godbolt.org/#compilers:!((compiler:clang341,options:'--std%3Dc%2B%2B11',source:'%23include+%3Cboost/bimap.hpp%3E%0A%0Atypedef+boost::bimap+%3Cint,+int%3E+ParamIDs%3B%0Aextern+const+ParamIDs+cParamIDs%3B%0Aextern+decltype(cParamIDs.left)%26+cParamIDsByAge%3B%0A%0A//+In+my+code+this+inits+the+variable+with+some+data+via+a+function.%0Aconst+ParamIDs+cParamIDs+%7B%7D%3B%0Aauto+%26cParamIDsByAge+%3D+cParamIDs.left%3B')),filterAsm:(directives:!t),version:3 – Leon Jun 13 '16 at 08:55
1

Solution 1

In the header file, declare the type of cParamIDsByAge without using auto and don't initialize it:

code.h

#include <boost/bimap.hpp>

typedef boost::bimap <int, int> ParamIDs;
extern const ParamIDs cParamIDs;

extern const decltype(cParamIDs.left)& cParamIDsByAge;
// or
extern decltype((cParamIDs.left)) cParamIDsByAge;
// or
extern const ParamIDs::left_map& cParamIDsByAge;

code.cpp

#include "code.h"

const ParamIDs cParamIDs {};
auto &cParamIDsByAge = cParamIDs.left;

Solution 2

Use a function instead of a variable:

code.h

#include <boost/bimap.hpp>

typedef boost::bimap <int, int> ParamIDs;
extern const ParamIDs cParamIDs;
inline auto& cParamIDsByAge() { return cParamIDs.left; }
Leon
  • 31,443
  • 4
  • 72
  • 97