views:

330

answers:

2

Hi, I had a function like this, that wasn't within a class:

// Gets the maximum number of tracks displayable
const utils::uint32 GetConstMaxSystemRange()
{
    return constMaxSystemNumber - constMinSystemNumber + 1;
}

It compiled fine in VS2005, but then I got linker errors for each file after the first one to include it, even though I was using Compile Guards. On a hunch, I surrounded it with a class like so:

class CDSLimitsAccess
{
public:
    // Gets the maximum number of tracks displayable
    static const utils::uint32 GetConstMaxSystemRange()
    {
        return constMaxSystemNumber - constMinSystemNumber + 1;
    }

protected:
    CDSLimitsAccess(){}
};

And bang! Fixed.

Question: WHY?

+13  A: 

Include guards only protect that function from being included into the same translation unit twice. But it won't protect that function from being included into the whole program twice.

As the function is non-inline, it violates the One Definition Rule if it appears more than once in the whole program. Make it inline to solve the problem, or put the definition into a implementation file, putting only its declaration in the header.

// Gets the maximum number of tracks displayable. possible fix:
inline utils::uint32 GetConstMaxSystemRange() {
    return constMaxSystemNumber - constMinSystemNumber + 1;
}

On a side note - better don't put const on a built-in return value. Temporaries of them can't be qualified with const / volatile. But that's also not necassary: You can't modify them anyway. It's ignored as far as i know. But it won't ever have an effect.

Johannes Schaub - litb
I get a new linker error when I compile with the definition outside the header, as something lower down the list can't find it. Inlining (which I should have done anyway) fixed it right up. Thanks!However, isn't inlining partly a compiler decision, and thus potentially unreliable as a solution?
deworde
yes, whether the compiler inlines calls to functions is up to it. declaring a function inline makes the function an inline function. disregarding of whether or not calls to it are actually inlined, the rules about inline functions regarding the ODR still are valid.
Johannes Schaub - litb
note that if you put the function inside your implementation file (which i recommend if your function is bigger or may depend on names you can't or won't put visible into the header), you have to remove the inline specifier.
Johannes Schaub - litb
+3  A: 

Because if it is a free-floating function, on each include, it would be compiled into the .obj file, and when it comes to linking, there are duplicates.

For class members, there is a different policy, which allows for duplicates - which are later handled by the linker properly.

You can put it into a unnamed namespace, or add a static before it, so it will become local to the translation unit. However, this is not the right way to fix it - just include the declaration in the header, and put the implementation into a file, and you'll be fine.

Anteru